LoginSignup
1
2

More than 3 years have passed since last update.

Measure-Object は簡単に計算誤差が発生するので使わないほうが良い

Last updated at Posted at 2019-08-05

Measure-Object で配列の合計を計算した際、計算誤差が発生しまくって大変だったため、再現方法と回避策をメモ。

本記事の概要と結論

Measure-Object で、100 に 0.000001 を 200 回足すと、誤差が発生する。誤差が発生する原因は、 Measure-Object が内部で浮動小数点数を利用しており、浮動小数点の演算時に誤差が発生するためである。(と推測している)
そのため、Measure-Object は、会計処理などの計算誤差が許されない領域に利用してはならない。
以下に、誤差発生の再現方法(=誤差の発生例)と回避策を記載する。

Measure-Object で計算誤差を発生させる

PS C:\> # decimal 型の 配列を作成。最初の要素は 100。
PS C:\> $list = @([decimal]100); 
PS C:\> # 配列に 0.000001 を追加し、Measure-Object で合計を計算する。これを 210 回繰り返す。
PS C:\> $tmp = 1..210 | %{ $list += [decimal]0.000001; ($list | measure -sum).Sum }
PS C:\> # 計算結果の 195番目から 205 番目までを出力する。すると 200 番目から誤差が発生している。
PS C:\> $tmp[195..205]
100.000196
100.000197
100.000198
100.000199
100.0002
100.000200999999
100.000201999999
100.000202999999
100.000203999999
100.000204999999
100.000205999999
PS C:\> 

Measure-Object の計算誤差を回避する方法

Measure-Object の計算誤差を回避する方法は、Measure-Object を使わずに、decimal 型で合計する(という方法しか思いつきませんでした!)

PS C:\> # Measure-Object の計算誤差を回避する方法
PS C:\> $list = @([decimal]100)
PS C:\> # $result 変数を作成し、配列の数だけ加算する。
PS C:\> $tmp = 1..210 | %{ $list += [decimal]0.000001; $result = 0; ($list | %{ $result += $_ }); $result }
# 計算結果の 200 番目以降も誤差が発生していない。
PS C:\> $tmp[195..205]
100.000196
100.000197
100.000198
100.000199
100.000200
100.000201
100.000202
100.000203
100.000204
100.000205
100.000206
PS C:\>

結論

Measure-Object は、会計処理などの計算誤差が許されない領域に利用してはならない。当たり前の結論です。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2