2017-09-18 4 views
3

저는 두 번의 반올림 된 값을 빼는 것이 왜 비 둥근 값을 갖게되는지 알아 내려고 애 쓰고 있습니다. 2.2 - 1.1 = 1.10000001 인 경우와 같습니다.PowerShell [수학] :: 라운드가 때때로 라운딩되지 않습니까?

내가 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 

에서 [수학] :: 라운드() 메소드가 의도 한대로 때문에, 작동하는 것 같다 $ sizeGB 및 $ freeSpaceGB 변수에 저장된 값이 반올림됨을 알 수 있습니다. 여기에 몇 가지 디버그 출력 예는 다음과 같습니다 어떤 경우에는 당신이 예를 들어, 이미지에 비 둥근 값이 개 이전에 반올림 값 결과의 뺄셈 볼 수 왜 이해가 안

debug output 2

  1. .
  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' back을 얻고있다. 그리고 나서 [부동 소수점 숫자가 정확히 어떤 숫자를 나타낼 수 없다는] 일반적인 문제를 겪는다. (https://stackoverflow.com/questions/1089018/why- cant-decimal-numbers-be-exactly-in-binary)로 표시됩니다. 중간 값이 아닌 최종 출력 만 반올림합니다. – BACON

답변

3

... 내가 $disks을 가정하고

FreeSpaceSize 속성은 정수 유형있는 경우 Win32_LogicalDisk 인스턴스의 모음입니다 (특히, UInt64). $disks에 다른 유형이 포함되어 있다면이 두 속성은 바이트 수를 처리하기 때문에 어떤 종류의 정수 일 가능성이 높습니다.

이 질문에 다소 불필요한이지만,이 줄은 ...

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

... $percentFree 그 메소드의 리턴 타입이기 때문에 당신이 the Math.Round overload that rounds to the nearest integer를 호출하는 경우에도 Double 포함됩니다. 당신은/원하는 당신과 같이 하나에 캐스팅 할 필요가 다음 정수 유형을 포함하는 $percentFree을 기대한다면 당신은

$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/1073741824 
$freeSpaceGB = $freespace/1073741824 
$usedSpaceGB = [Math]::Round($sizeGB - $freeSpaceGB, 2) 

...

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

보고있는 것처럼 부동 소수점 근사를 제거하지는 않지만 $sizeGB$freeSpaceGB의 이미 반올림 된 값 (일부 정보는 삭제 함)을 기반으로 계산하지 않으므로 $usedSpaceGB은 실제 값에 더 가깝습니다.

이전 스 니펫에서 $usedSpaceGB은 여전히 ​​Double이므로 정확히 나타낼 수없는 값으로 계산할 수도 있습니다. 이 값이 표시 포맷하는 또는 기타에 직렬화해야하기 때문에 string (HTML로) 난 그냥에 대한 Math.Round을 잊고 string formatting는이 같은 당신을 위해 반올림을 처리하도록 ...

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

을 ... 또는 것 이 ...

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

.ToString ('N2')에 대한 Math.Round를 잘 변경하면 문제가 해결됩니다. 감사. – smartdan