PowerShell [Math]::Round 有时不舍入?

标签 powershell rounding

我很难弄清楚为什么有时两个舍入值的减法会给我一个非舍入值。就像如果 2.2 - 1.1 = 1.10000001。

我在 PowerShell 中有这个脚本 block :

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 变量中的舍入值。以下是一些调试输出示例:

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

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

    enter image description here

    现在我通过舍入也“修复”了减法结果,但我想知道为什么会这样。我希望有人可以帮助我了解发生了什么。

    最佳答案

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

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

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

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

    ... $percentFree将包含 Double即使你调用 the Math.Round overload that rounds to the nearest integer因为那是该方法的返回类型。您可以通过评估来检查...
    $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)
    

    关于PowerShell [Math]::Round 有时不舍入?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46286675/

    相关文章:

    c++ - 四舍五入到小数点后第二位

    C:如何舍入一个全局变量?

    windows - IE 通过注册表启用/禁用代理设置

    windows - Win 10 WSL 不会设置默认值 2

    java - 使用 decimalFormat 错误设置精度 - Java

    algorithm - 你能帮我确定这个应用程序的四舍五入吗?

    SQL 舍入和截断,需要彻底解释

    json - PowerShell ConvertFrom-Json 未将 JSON 列表转换为 Json 对象

    c# - 如何启用 Windows 10 "Containers"功能?

    powershell - `Start-Process` 找不到存在于 PATH 中的文件,即使给定了文件的绝对路径