PowerShell [数学]::舍入有时不舍入? [英] PowerShell [Math]::Round sometimes not rounding?
问题描述
我一直在努力弄清楚为什么有时两个四舍五入的值相减会得到一个非四舍五入的值.就像 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:
- 我不明白为什么在某些情况下,两个先前舍入的值相减会导致非舍入值,如您在示例图像中所见.
- 这种行为发生在循环的相同位置,比如第 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
实例,在这种情况下,FreeSpace
和 Size
属性是整数类型(特别是 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屋!