PowerShell [数学]::舍入有时不舍入? [英] PowerShell [Math]::Round sometimes not rounding?

查看:79
本文介绍了PowerShell [数学]::舍入有时不舍入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在努力弄清楚为什么有时两个四舍五入的值相减会得到一个非四舍五入的值.就像 2.2 - 1.1 = 1.10000001.

I'm having struggle trying to figure out why some times the substraction of two rounded values gives me a non-rounded value. Is like if 2.2 - 1.1 = 1.10000001.

我在 PowerShell 中有这个脚本块:

I have this block of a script in PowerShell:

foreach($disk in $disks) 
{
    $size = $disk.Size
    $freespace = $disk.FreeSpace
    $percentFree = [Math]::Round(($freespace / $size) * 100)
    $sizeGB = [Math]::Round($size / 1073741824, 2)
    $freeSpaceGB = [Math]::Round($freespace / 1073741824, 2)
    $usedSpaceGB = $sizeGB - $freeSpaceGB

    #view the variable values in every iteration
    Write-Debug "`$size = $size"
    Write-Debug "`$freespace = $freespace"
    Write-Debug "`$percentFree = $percentFree%"
    Write-Debug "`$sizeGB = $sizeGB"
    Write-Debug "`$freeSpaceGB = $freeSpaceGB"
    Write-Debug "`$usedSpaceGB = $usedSpaceGB"
}

令人不安的部分是:

$usedSpaceGB = $sizeGB - $freeSpaceGB

[Math]::Round() 方法似乎按预期工作,因为我可以看到存储在 $sizeGB 和 $freeSpaceGB 变量中的舍入值.以下是一些调试输出示例:

The [Math]::Round() method seems to work as intended, because I can see the rounded values stored in $sizeGB and $freeSpaceGB variables. Here are some debug output examples:

  1. 我不明白为什么在某些情况下,两个先前舍入的值相减会导致非舍入值,如您在示例图像中所见.
  2. 这种行为发生在循环的相同位置,比如第 7、10 和 18 次迭代.

我将这些数据导出到一个 HTML 文件中,因此表格看起来很奇怪,因为这个值没有四舍五入:

I export this data to an HTML file so the table looks odd with this values not rounded:

现在我通过舍入减法结果来修复"这个问题,但我想知道为什么会发生这种情况.我希望有人能帮助我了解发生了什么.

Right now I "fixed" this by Rounding also the subtraction result but I'm wondering why is this happening. I hope someone can help me to understand what is going on.

推荐答案

好吧,让我们试试这个作为答案...

Alright, let's try this as an answer...

我假设 $disks 的集合Win32_LogicalDisk 实例,在这种情况下,FreeSpaceSize 属性是整数类型(特别是 UInt64).如果 $disks 包含一些其他类型,那么这两个属性可能是某种整数,因为我们正在处理字节数.

I am assuming $disks is a collection of Win32_LogicalDisk instances, in which case the FreeSpace and Size properties are integer types (specifically, UInt64). If $disks contains some other type(s), those two properties are likely integers of some kind since we're dealing with byte counts.

这与问题有些无关,但在这一行...

This is somewhat extraneous to the question, but in this line...

$percentFree = [Math]::Round(($freespace / $size) * 100)

...$percentFree 将包含 Double 即使您正在调用 四舍五入到最接近的整数的Math.Round 重载 因为这是该方法的返回类型.您可以通过评估...来检查这一点

...$percentFree will contain a Double even though you're calling the Math.Round overload that rounds to the nearest integer because that's the return type of that method. You can inspect this by evaluating...

$percentFree.GetType()

如果您希望/期望 $percentFree 包含整数类型,那么您需要将其强制转换为 1,就像这样...

If you want/expect $percentFree to contain an integer type then you need to cast it to one, like this...

$percentFree = [UInt64] [Math]::Round(($freespace / $size) * 100)

当您使用 Math.Round 舍入到最接近的百分位时,上述内容也适用...

The above also applies when you are using Math.Round to round to the nearest hundredth...

$sizeGB = [Math]::Round($size / 1073741824, 2)
$freeSpaceGB = [Math]::Round($freespace / 1073741824, 2)

...当然,因为该方法重载必须返回浮点类型,因为根据定义,整数类型不能存储值的小数部分.因此,$sizeGB$freeSpaceGB 包含浮点值(特别是 Double),所以当这一行...

...because, of course, that method overload has to return a floating-point type since, by definition, an integer type could not store fractions of a value. Thus, $sizeGB and $freeSpaceGB contain floating-point values (specifically Double) so when this line...

$usedSpaceGB = $sizeGB - $freeSpaceGB

...被执行 $usedSpaceGB 也将包含一个 Double,在这种情况下,所有的赌注都关闭了 能够准确地表示结果值.

...is executed $usedSpaceGB will also contain a Double, in which case all bets are off as far as it being able to exactly represent the resulting value.

希望这能解释为什么会发生这种情况.至于如何改进您的代码,首先我建议不要对中间值进行任何舍入...

Hopefully that explains why this is happening. As far as how to improve your code, first I would recommend not doing any rounding on intermediate values...

$sizeGB = $size / 1073741824
$freeSpaceGB = $freespace / 1073741824
$usedSpaceGB = [Math]::Round($sizeGB - $freeSpaceGB, 2)

...可以更清晰简洁地写成...

...which can be written more clearly and concisely as...

$sizeGB = $size / 1GB
$freeSpaceGB = $freespace / 1GB
$usedSpaceGB = [Math]::Round($sizeGB - $freeSpaceGB, 2)

这不会像您看到的那样消除浮点近似值,但是 $usedSpaceGB 将更接近实际值,因为您不是基于已经舍入的(丢弃一些信息)$sizeGB$freeSpaceGB 的值.

This won't eliminate floating-point approximations like you're seeing, but $usedSpaceGB will be closer to the actual value since you're not computing based on the already-rounded (which discards some information) values of $sizeGB and $freeSpaceGB.

在前面的片段中,$usedSpaceGB 仍然是一个 Double,因此它仍然可能计算出一些无法准确表示的值.由于此值正在格式化以显示或以其他方式必须序列化为 string(作为 HTML),我会忘记 Math.Round 并让 string 格式 为您处理舍入像这样...

In the previous snippets, $usedSpaceGB is still a Double so it's still possible it computes to some value that can't be represented exactly. Since this value is being formatted for display or otherwise must be serialized to a string (as HTML) I would just forget about Math.Round and let string formatting handle the rounding for you like this...

$usedSpaceGBText = (($size - $freeSpace) / 1GB).ToString('N2')

...或者这个...

$usedSpaceGBText = '{0:N2}' -f (($size - $freeSpace) / 1GB)

这篇关于PowerShell [数学]::舍入有时不舍入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆