浮動小数点数のシステムは、 絶対値が大きな数や絶対値が小さな数は全く表現できないという 性質がある。
計算の結果生じるはずの値の絶対値が小さくなって浮動小数点数では表現できな くなったとき「アンダーフローが生じる」と言い、例外を発生したり、 値を に置き換えて計算を続行したりする。
ところで丸めモードを up にしておくと、アンダーフローが起こらない。それを 見るのが次の実験である。 を で割る操作を続けて行って になって しまうかどうか調べている。
以下のプログラムでは、 x を で割ってから、x の値が に等しいかどうかチェックする間に、「意味のない」実行文をはさんでいること に注意してもらいたい。これをしないと、x は CPU の register に載っ たままになり、register 上で表現可能な値の範囲は long double のそれと (ほ ぼ) 一致してしまい1、簡単 にはアンダーフローしなくなる (long double で表現可能な値の範囲を越えて初 めてアンダーフローするので)。
test-round0.c |
/* * test-round0.c * 数列 2^{-n} を計算すると、大きな n に対してアンダーフローして 0 に なってしまう、というのが常識であるが、丸めモードが up であると そうはならない。 */ #include "round.h" #include <stdio.h> void dummy() {} void try() { int i, n; double x, oldx; n = 10000; x = 1.0; for (i = 1; i <= n; i++) { oldx = x; x /= 2; /* 何か挟まないと x がレジスターに載っていて、 long double のレンジを越えない限りアンダーフローしない */ dummy(); if (x == 0.0) { printf("2^{-%d} が 0 になりました。\n", i); printf("2^{-%d+1}=%g\n", i, oldx); break; } } if (i > n) { printf("%d 回 2 で割っても 0 にはなりませんでした。x=%g\n", n, x); } } int main() { Near(); printf("Near\n"); try(); Down(); printf("Down\n"); try(); Up(); printf("Up\n"); try(); return 0; } |
test-round0 の実行結果 |
Near 2^{-1075} が 0 になりました。 2^{-1075+1}=4.94066e-324 Down 2^{-1075} が 0 になりました。 2^{-1075+1}=4.94066e-324 Up 10000 回 2 で割っても 0 にはなりませんでした。x=4.94066e-324 |