14章.浮動小数点



14.1:
浮動小数点の変数を、例えば3.1に設定すると、どうしてprintf()は 3.0999999と出力するのか。

A:

たいていのコンピューターは浮動小数点の数に、整数にと同じように、 2進数を用いている。2進数の1/1010(これは10進数では1/10)は無限循 環小数となる。2進表現は0.0001100110011...となる。コンパイラー の2進/10進変換ルーチン(たとえばprintf()で使われているもの)がど れくらい注意深く作られているかによって、2進数では正確に表現で きない数が(特に精度の低い浮動小数が)代入されたり読み込まれて出 力されると(すなわち、2進から10進に変換し、10進から2進に変換し なおすと)食い違いが生じるかもしれない。質問14.6も参照のこと。


14.2:
平方根を求めようとしている。けれど、とんでもない数字が返ってく る。

A:

まず<math.h>を#includeしたことを確認して、次にdoubleを返す関数 を正しく宣言したことを確認する。(その他のライブラリールーチン で注意しなければいけないのは、atof()である。これは<stdlib.h>で 宣言されている。) 下の質問14.3も参照のこと。

References:
CT&P Sec. 4.5 pp. 65-6.


14.3:
ちょっとした三角関数の計算がしたくて<math.h>を#includeしたけれ ど「_sin未定義」というコンパイルエラーが返ってきた。

A:

まずは正しい数学関数のライブラリーがリンクされていることを確認す ること。たとえばUnixではコンパイルあるいはリンクをする時に、コ マンドの一番最後に-lmオプションをつける必要がある。質問13.2513.26も参照のこと。


14.4:
浮動小数点の計算の結果が変で、しかもマシンによって違った答が返っ てくる。

A:

まずは上の質問14.2を参照すること。

問題がそんなに単純でないときは、デジタルコンピュータが使ってい る浮動小数のフォーマットは、実数の演算のよくできたシミュレーショ ンを与えるものであって、決して正確なシミュレーションを与えるも のではないことをお忘れなく。桁落ちや、精度が徐々に失われていく ことが問題になることも多い。

浮動小数の結果が正確なんて考えないこと。特に、浮動小数で表され た値が比較に使えるなんて考えないこと(いんちきな"fuzz factor"を 使うこともね。質問14.5参照)。

これらはC言語だけでなく他のプログラム言語でも問題である。浮動 小数点の実装の多少の部分は、通常「プロセッサーがやることはなん でもあり」に定義されている。そうでなければ"正しい"浮動小数点の モデルを持たないマシンではエミュレーションをするしかなく、そん なことになればコードのサイズと処理速度が犠牲になって使い物にな らない。

この記事では、浮動小数点を扱う際の落とし穴や抜け道をいちいち挙 げることはできない。よくできたプログラミングの教科書は、基礎的 な事柄を教えてくれる。以下の参考文献も参照のこと。

References:
Kernighan and Plauger, _The Elements of Programming Style_ Sec. 6 pp. 115-8; Knuth, Volume 2 chapter 4; David Goldberg, "What Every Computer Scientist Should Know about Floating-Point Arithmetic".


14.5:
2つの浮動小数点の値が"十分近い"ことを判定するよい方法は。

A:

浮動小数点の値の絶対的な精度は、浮動小数点の定義により、その大 きさによって変化するので、二つの浮動小数点の値を比較する最上の 方法は精度の敷居を使うことである。精度の敷居は、比較しあう数の 大きさに比例させる。

double a, b;
...
if(a == b) /* 間違い */

ではなく、以下のコードを

#include <math.h>

if(fabs(a - b) <= epsilon * a)

適切に選んだepsilonと共に使え。

References:
Knuth Sec. 4.2.2 pp. 217-8.


14.6:
四捨五入の仕方は。

A:

一番単純で容易なのは以下のようなコードを書くことである。

(int)(x + 0.5)

しかしこの方法は負の数に対してはうまくいかない。


14.7:
なぜCに、べき乗が組み込み関数で用意されていないのか。

A:

べき乗を組み込みの命令として持っているプロセッサーが少ないか らである。かわりに#include <math.h>して関数pow()を使えばよい。 ただし小さな整数が対象なら、たいていは自分で掛け算で書いたほう がよい。

References:
ANSI Sec. 4.5.5.1; ISO Sec. 7.5.5.1; H&S Sec. 17.6 p. 393.


14.8:
私が使ってるマシンの<math.h>からあらかじめ定義されているはずの 定数M_PIが漏れているようだ。

A:

この定数は(これはπの値を、マシンの精度一杯まで正確に、表わし ているに違いない)標準ではない。πが必要ならば、自分で#defineす ること。

References:
PCS Sec. 13 p. 237.


14.9:
IEEEのNanや、その他の特別な値かどうかのテストはどうやればよい か。

A:

IEEEの浮動小数を高品質に実装しているシステムの多くは、これらの 値のテストを簡潔に行う機能を(例えばあらかじめ定義した定数や isnan()マクロを<math.h>または<ieee.h>か<nan.h>に)用意している。 上のようなテストの標準化の作業が続けられている。Nanかどうかの 力ずくだけれど、たいていはうまくいくテスト方法を以下の例で示す。

#define isnan(x) ((x) != (x))

けれどIEEEフォーマットを知らないコンパイラは、このテストを最適 化の結果、削除するかもしれない。

その他の方法としては、問題にしている値をsprintf()で整形するこ とが考えられる。多くのシステムでsprintf()は「NaN」とか「Inf」 といった文字列を生成するので、困ったときには役に立つ。


14.11:
Cで複素数を実装するよい方法は。

簡単なのは、単純な構造体とその構造体を操作する算術関数をいくつ か用意することである。質問2.7, 2.10, 14.12を参照のこと。


14.12:
以下の仕事をするコードを探している。 高速フーリエ変換(FFT's) 行列演算(乗算、逆行列など) 複素数演算

A:

Ajay Shahがフリーで手に入る算術ソフトウェアの索引を管理してい る。定期的に登校されている。このFAQリストが入手できるのと同じ 場所に保存されている(質問20.40参照)。質問18.16も参照のこと。


14.13:
Turbo Cでプログラムを実行すると「浮動小数点フォーマットがリン クされていない」といってクラッシュするので困っている。

A:

小規模マシン用のコンパイラの中には、Borland社のC(Dennis RitchieのオリジナルのPDP-11用のコンパイラも)も含めて、必要がな さそうにみえるときは浮動小数点への対応を実行しないようにできて いるものがある。特に浮動小数点に対応しないprintf()やscanf()は %e、%f、%gを扱うコードを省略することでメモリを節約している。 Borlandの、プログラムが浮動小数点を使っているかどうかを判定す る規則は、たまたま不十分なようである。そんなときはプログラマー は浮動小数に対応したコードをロードするために、明示的に浮動小数 点を使ったライブラリールーチンをよばなければならない(詳細は comp.os.msdos.programmer FAQを参照のこと)

(訳注:Turbo Cではソースファイル内にextern void _ floatconvert(); #pragma extref _floatconvertと明示的に書くこと で、このエラーを回避することができる。)

目次へ戻る