KDOC 246: 浮動小数点の誤差を体感する
この文書のステータス
- 作成
- 2024-09-21 貴島
- レビュー
- 2024-10-05 貴島
概要
本を読んでいて、浮動小数点数で数値比較してはいけない、とあった。代数的に等しくても、誤差によってビット全体を比較すると等しくならないことがある。確かめてみる。
void calc(double original, double d1, char ope, double d2) {
double result;
switch (ope) {
case '+':
result = d1 + d2;
break;
case '-':
result = d1 - d2;
break;
case '*':
result = d1 * d2;
break;
case '/':
result = d1 / d2;
break;
};
printf("%s", "┏ ─────────────────────────────────────────────────────────────────── ┓\n");
printf("original: \t%.55f\nresult: \t%.55f\n - d1: \t%.55f\n - ope: \t%c\n - d2: \t%.55f\n", original, result, d1, ope, d2);
printf("equal? => %d\n", (original == result));
printf("%s", "┗ ─────────────────────────────────────────────────────────────────── ┛\n\n");
};
calc(0.1, 0.05, '+', 0.05);
calc(1, 0.5, '+', 0.5);
calc(0.3, 0.1, '+', 0.2);
calc(0.1, 0.09, '+', 0.01);
calc(0.01, 0.23, '-', 0.22);
calc(0.1, 1.0, '-', 0.9);
┏ ─────────────────────────────────────────────────────────────────── ┓
original: 0.1000000000000000055511151231257827021181583404541015625
result: 0.1000000000000000055511151231257827021181583404541015625
- d1: 0.0500000000000000027755575615628913510590791702270507812
- ope: +
- d2: 0.0500000000000000027755575615628913510590791702270507812
equal? => 1
┗ ─────────────────────────────────────────────────────────────────── ┛
┏ ─────────────────────────────────────────────────────────────────── ┓
original: 1.0000000000000000000000000000000000000000000000000000000
result: 1.0000000000000000000000000000000000000000000000000000000
- d1: 0.5000000000000000000000000000000000000000000000000000000
- ope: +
- d2: 0.5000000000000000000000000000000000000000000000000000000
equal? => 1
┗ ─────────────────────────────────────────────────────────────────── ┛
┏ ─────────────────────────────────────────────────────────────────── ┓
original: 0.2999999999999999888977697537484345957636833190917968750
result: 0.3000000000000000444089209850062616169452667236328125000
- d1: 0.1000000000000000055511151231257827021181583404541015625
- ope: +
- d2: 0.2000000000000000111022302462515654042363166809082031250
equal? => 0
┗ ─────────────────────────────────────────────────────────────────── ┛
┏ ─────────────────────────────────────────────────────────────────── ┓
original: 0.1000000000000000055511151231257827021181583404541015625
result: 0.0999999999999999916733273153113259468227624893188476562
- d1: 0.0899999999999999966693309261245303787291049957275390625
- ope: +
- d2: 0.0100000000000000002081668171172168513294309377670288086
equal? => 0
┗ ─────────────────────────────────────────────────────────────────── ┛
┏ ─────────────────────────────────────────────────────────────────── ┓
original: 0.0100000000000000002081668171172168513294309377670288086
result: 0.0100000000000000088817841970012523233890533447265625000
- d1: 0.2300000000000000099920072216264088638126850128173828125
- ope: -
- d2: 0.2200000000000000011102230246251565404236316680908203125
equal? => 0
┗ ─────────────────────────────────────────────────────────────────── ┛
┏ ─────────────────────────────────────────────────────────────────── ┓
original: 0.1000000000000000055511151231257827021181583404541015625
result: 0.0999999999999999777955395074968691915273666381835937500
- d1: 1.0000000000000000000000000000000000000000000000000000000
- ope: -
- d2: 0.9000000000000000222044604925031308084726333618164062500
equal? => 0
┗ ─────────────────────────────────────────────────────────────────── ┛
末尾付近にある数値によって桁上がりした場合に、大きな誤差となることがあるのだが、末尾の数値はなぜ出てくるのだろうか。2進数で表現できないというのはわかるのだが、小数第20位まで辿らないと見えないような値なのはなぜなのだろう。
0.5は2^(-1)なので、正確に表現できる。小数以下の数字は現れない。
関連
抽象。
- 数学的に等しい値が、表現の限界による誤差で等しいと判定されない
- 有限の桁でぴったり表せる値と表せない値がある
関連。
- 動機: KDOC 192: 『Write Great Code Vol.1』。例を確かめた
- 類推: KDOC 229: 浮動小数点を手計算する。同様に浮動小数点の話題