我一直在努力弄清楚为什么有时候两个舍入值的减法给我一个非舍入值。就像2.2 - 1.1 = 1.10000001。
我在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变量中的舍入值。以下是一些调试输出示例:
答案 0 :(得分:4)
好吧,让我们试试这个......
我假设$disks
是Win32_LogicalDisk
个实例的集合,在这种情况下,FreeSpace
和Size
属性是整数类型(具体来说,UInt64
) 。如果$disks
包含其他一些类型,那么这两个属性可能是某种整数,因为我们正在处理字节数。
这与这个问题有点无关,但在这一行......
$percentFree = [Math]::Round(($freespace / $size) * 100)
... $percentFree
即使您正在调用the Math.Round
overload that rounds to the nearest integer,也会包含Double
,因为这是该方法的返回类型。您可以通过评估......
$percentFree.GetType()
如果您希望/期望$percentFree
包含整数类型,那么您需要将其强制转换为一个,如下所示...
$percentFree = [UInt64] [Math]::Round(($freespace / $size) * 100)
当您使用Math.Round
舍入到最接近的百分之一时,上述情况也适用
$sizeGB = [Math]::Round($size / 1073741824, 2)
$freeSpaceGB = [Math]::Round($freespace / 1073741824, 2)
...因为,当然,该方法重载必须返回一个浮点类型,因为根据定义,整数类型不能存储值的分数。因此,$sizeGB
和$freeSpaceGB
包含浮点值(特别是Double
)所以当这一行......
$usedSpaceGB = $sizeGB - $freeSpaceGB
...已执行$usedSpaceGB
还将包含Double
,在这种情况下,所有投注均已关闭being able to exactly represent the resulting value。
希望这能解释为什么会这样。至于如何改进代码,首先我建议不要对中间值进行任何舍入...
$sizeGB = $size / 1073741824
$freeSpaceGB = $freespace / 1073741824
$usedSpaceGB = [Math]::Round($sizeGB - $freeSpaceGB, 2)
......可以更清晰,更简洁地写成......
$sizeGB = $size / 1GB
$freeSpaceGB = $freespace / 1GB
$usedSpaceGB = [Math]::Round($sizeGB - $freeSpaceGB, 2)
这不会消除您所看到的浮点近似值,但$usedSpaceGB
将更接近实际值,因为您不是基于已经舍入的(放弃某些信息)值进行计算$sizeGB
和$freeSpaceGB
。
在之前的代码段中,$usedSpaceGB
仍然是Double
,所以它仍然可能会计算出一些无法准确表示的值。由于此值正在格式化以供显示,或者必须序列化为string
(作为HTML),我会忘记Math.Round
并让string
formatting像这样处理舍入。
$usedSpaceGBText = (($size - $freeSpace) / 1GB).ToString('N2')
......或者......
$usedSpaceGBText = '{0:N2}' -f (($size - $freeSpace) / 1GB)