2017-09-18 15 views
3

2つの丸められた値の減算が私に非丸められた値を与える理由を理解しようと苦労しています。 2.2 - 1.1 = 1.10000001の場合と似ています。PowerShell [数学] ::ラウンドは丸められないことがありますか?

私はPowerShellでスクリプトのこのブロックを持っている:、

$usedSpaceGB = $sizeGB - $freeSpaceGB 

[数学] ::ラウンド()メソッドが意図したとおりに動作しているようですので:厄介な部分がある

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" 
} 

$ sizeGBと$ freeSpaceGB変数に丸められた値が格納されているのがわかります。ここではいくつかのデバッグ出力例は以下のとおりです。

debug output 2

  1. あなたは例の画像で見ることができるようにいくつかのケースでは2の減算は、以前に非丸めた値の値の結果を丸め、なぜ私は理解していません。
  2. この現象は、ループの同じ正確な位置で発生し、7,10、および18回の繰り返しで発生します。

の表は、この値は丸められていないと奇妙に見えるので、私は、HTMLファイルにこのデータをエクスポート:

enter image description here

を今、私は「固定」も減算結果、この丸めではなく、私は」なぜこれが起こっているのだろうかと疑問に思います。誰かが私に何が起こっているのか理解するのを助けてくれることを願っています

よし
+7

私は '$ disks'が[' Win32_LogicalDisk'](https://msdn.microsoft.com/library/aa394173.aspx)のインスタンスだとしますか?もしそうなら、 'FreeSpace'と' Size'は整数型(両方とも 'UInt64')ですが、[Math.Round']に渡します(https://msdn.microsoft.com/library/system.math.round .aspx)あなたは 'Double'バックを取得しています。これは[浮動小数点数がいくつかの数字を正確に表すことができない]という通常の問題の影響を受けます(https://stackoverflow.com/questions/1089018/why- cant-decimal-numbers-be-exactly-in-binary)を使用します。私は中間の値ではなく最終的な出力のみを丸めます。 – BACON

答えて

3

、の答えとしてこれを試してみましょう...

私は$disksFreeSpaceSizeプロパティは、整数型(具体的には、UInt64)である場合にはWin32_LogicalDiskインスタンスの集まりであると仮定しています。 $disksに他の型が含まれている場合、これらの2つのプロパティは、バイト数を処理するため、ある種の整数である可能性があります。

これは質問にやや異質のですが、このラインでは...

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

... $percentFreeはそれがそのメソッドの戻り値の型ですので、あなたがthe Math.Round overload that rounds to the nearest integerを呼んでいるにもかかわらず、Doubleが含まれています。あなたは

...あなたは/が $percentFreeは整数型を含めることを期待したいなら、あなたはこのように、1にキャストする必要があります...評価して
$percentFree = [UInt64] [Math]::Round(($freespace/$size) * 100) 

$percentFree.GetType() 

これを上記検査することができますあなたは最寄りの百に丸めるMath.Roundを使用している場合は、当然のことながら、そのメソッドのオーバーロードはdで、以来、浮動小数点型を返すことがあるため、また... ...

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

を適用efinitionでは、整数型は値の端数を格納できませんでした。したがって、$sizeGB$freeSpaceGBを含む浮動小数点値(具体的Double)ので、このライン...

$usedSpaceGB = $sizeGB - $freeSpaceGB 

... $usedSpaceGBはまた、すべてのベットは限りオフである場合にDoubleを含むであろうが実行されますそれはbeing able to exactly represent the resulting valueです。

これがなぜ起こっているのかを説明してください。限り、あなたのコードを改善する方法として、最初に私が任意の中間値に丸めをしていないお勧めします...

$sizeGB = $size/1GB 
$freeSpaceGB = $freespace/1GB 
$usedSpaceGB = [Math]::Round($sizeGB - $freeSpaceGB, 2) 
...としてより明確かつ簡潔に書くことができ

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

...

これは浮動小数点数の近似を排除するものではありませんが、$usedSpaceGBは、すでに丸められた(情報を破棄する)値$sizeGBおよび$freeSpaceGBに基づいて計算していないため、実際の値に近い値になります。

前のスニペットでは、$usedSpaceGBはまだDoubleであるため、正確に表現できない値も計算できます。この値は、表示用にフォーマットされているか、そうでなければ、私はちょうど約Math.Roundを忘れてstring formattingは、このようにあなたのために丸めを処理させるでしょう(HTMLなど)stringにシリアライズされなければならないので...

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

...かこれは...

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

.ToString( 'N2')のMath.Roundをうまく変更すると、問題が解決しました。ありがとう。 – smartdan

関連する問題