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: 浮動小数点を手計算する。浮動小数点で関連している