C・C++の浮動小数点演算誤差に関するお薦め参考書

C・C++において、doubleをint32に格納する際に丸めを考慮しなければならない、負数かどうかの判断に単純に0と比較するだけでは不十分な事があるなどの、浮動小数点の演算誤差の扱いに自信が持てずにいます。
日本語もしくは英語で学習するのに良い書籍を紹介して頂けませんでしょうか。

検索していると1.0e-7がマジックワードの様に出てきますが、こういった数字の数学・コンピューターサイエンス的な背景も学びたいと思っています。

できれば「こうすればOK」というTips本だけではなく、どういう事を考慮せねばならず、このやり方をするとなぜOKになるのか、と言う事が自分で考えられるようになる書籍を紹介して頂けると嬉しいです。

よろしくお願いします。

回答の条件
  • 1人1回まで
  • 登録:
  • 終了:2014/12/23 10:35:04
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:a-kuma3 No.1

回答回数4974ベストアンサー獲得回数2154

ポイント300pt

https://docs.oracle.com/cd/E19422-01/819-3693/819-3693.pdf
Sun の "Numerical Computation Guide" という文書です。

まずは、2.2.3 Double Format の確認辺りから、初めてはいかがでしょうか。
負の値かどうかを正確に判定するなら、負号ビットで判定します。
フォーマット上は、+0.0 と -0.0 があります。

もうちょっと突っ込んだところは、APPENDIX D の
What Every Computer Scientist Should Know About Floating-Point Arithmetic を読むと良いと思います。

検索していると1.0e-7がマジックワードの様に出てきますが

想像ですけど、Single Format (c で言うところの float) では仮数部が 23bit です。
十進数にすると八桁にちょっと届きません。
そんなところの名残なんじゃないかと思います(全くの想像です)。

id:kyo_bracer

ありがとうございました。
早速年末の時間を使って読ませて頂きました。

2015/01/02 03:55:55
  • id:jwrekitan
    コンピューターっていうのは元々小数点は扱えないんですよ。
    だけどシフト演算(C言語でも扱えるけれど元々はCPUの命令)
    によって元の数の半数とかにはできるので、

    1の半分の0.5、0.5の半分の0.25、0.25の半分の0.125

    というのを正確に表現する事は可能だし、これらを組み合わせた、

    0.625(0.5+.0125)、0.75(0.5+0.25)、0.825(0.5+0.25+0.125)

    なんかも表現可能なんですね。ここで考慮してるのは3ビットだけですが、
    ビット数を増やせばもっと穴が少なくなります。
    こうした基本を理解したうえで↓を読んでみるといいかも。

    http://dobon.net/vb/dotnet/beginner/floatingpointerror.html
  • id:a-kuma3
    椶櫚>コンピューターっていうのは元々小数点は扱えないんですよ。
    椶櫚>だけどシフト演算(C言語でも扱えるけれど元々はCPUの命令)
    椶櫚>によって元の数の半数とかにはできるので、
    それは違います(というか、大雑把すぎ)。

    紙で計算するときだって、十進数で割り切れない少数を扱うときには有限桁で打ち切って計算するわけだし、最初に身近なのが十進数で、コンピュータは二進数で扱っているというだけ。
    どちらが精度が高い、というわけではない。
    三進数の浮動小数点を考えれば、十進数で割り切れない 1/3 も正確に表すことができるのだし。

    人間に合わせて十進数で演算する BCD 演算ライブラリというのもある(遅いけど、十進数と二進数の精度の差で悩むことがない)し、有理数を扱えるライブラリだって作れます(というか、多分ある)。

  • id:jwrekitan
    三進数 … 一瞬納得しそうになったんですけれども、
    その理論でいくと1/7を表すには七進数が必要で、
    1/11を表すには十一進数が必要で、 … となるような気が^^;
    理論上表現できても2進数に置き換えられないと意味無いですよ。
    だってCPUが2進数で計算する事を前提としているのですから。
    (それに、表現できる事と計算できる事は別次元の問題)

    > どちらが精度が高い、というわけではない。

    というのも確かにその通りなんですが、私もそんな話はしていないです。
    (あくまで内部での数値の扱いに言及したまでです)
    電子回路における基本中の基本なので、知っている人は知っているし、
    同じような解説は探せばそこら中にあります。
    だって教える立場の人がそういう教え方をしているんだもの(笑)。

    BCDにおける小数点計算については
    http://itpro.nikkeibp.co.jp/members/ITPro/ITBASIC/20010719/3/
    >>
    ところで,ゾーン10進数形式でもパック10進数形式でも,小数点数は表せません。
    小数点を表す手段が定められていないからです。
    それでは,BCDでは,小数点数の計算ができないのでしょうか?
    実は,BCDで小数点数の計算が必要な場合には,プログラマの判断で,
    整数のどこかに小数点のドットがあると仮定して計算するようにしているのです。
    <<
    とあり、結局、1バイト(8ビット)で10進2桁しか表せないわけで、

    整数部・小数部を合わせて何桁までの精度を扱えるように
    コンパイラの仕様を設計するか

    という話になってしまいます(この辺は仕様書に書いてあるとは思いますが)。
    まさか無限に配列を確保するプログラムを組むわけにもいきませんし。
    もっとも、↓のような情報もあるので、
    http://www.wizforest.com/diary/140720.html
    大雑把すぎという指摘は確かにその通りなんでしょうね。



    kyo_bracerさんの質問に話を戻すと、

    > doubleをint32に格納する際に丸めを考慮しなければならない

    というのは浮動小数点を整数に変換する事なので、
    どのような挙動になるのかはコンパイラ次第、という事になるでしょう。
    実際の動作は、整数化する際に小数点以下を切り捨てるのか、
    それとも四捨五入するのかのどちらかでしょうね。
    どちらなのかはテストプログラムを作って
    (例えば1.5が1になるのか2に変化するのか)
    挙動を確認すればいいだけの話になります。

    上の例で、四捨五入して2になって欲しいのに1になってしまうなら、
    0.5を足してから変数の受け渡しをすればいいでしょうし、
    その逆であれば0.5を引いてから変数の受け渡しをすればいいのです。
    こちらについては何も難しく考える事はないと思うのですが。


    他に気づいた点としては、
    「桁落ち」などのキーワードで検索すると良い情報が見つかるかも?
  • id:a-kuma3
    >というのは浮動小数点を整数に変換する事なので、
    >どのような挙動になるのかはコンパイラ次第、という事になるでしょう。
    簡単に書いてくれる :-)

    特に C言語(含む C++)は、規格がきっちり決まっているので、そういう話をするなら、規格を引用すべき。

    http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
    ちょっと古いので、申し訳ないんだけど、ぱっとアクセスできるのがこれなんで。
    浮動小数点から整数への変換は、H.2.4 Type conversions @p484 の辺りで触れられています。
    >>
    In the above conversions from floating to integer,the use of (cast)x can be replaced with (cast)round(x), (cast)rint(x), (cast)nearbyint(x), (cast)trunc(x), (cast)ceil(x),or (cast)floor(x).
    <<
    規格上は、切り上げ (ceil) も認められています。


    ぼくのコメントに返してもらっている部分は、ぼくの書きっぷりが良くないのだろうけれど、そういうことが言いたいのではないです。
    「コンピューターっていうのは元々小数点は扱えないんですよ。」ってところを指摘しているわけで、コンピュータだけが少数を扱えないわけではなくて、コンピュータは二進数で有限桁の小数を扱っていて、人間は十進数で有言桁を扱っているだけの話。
    丸め誤差や桁落ちの話をするのに、何進数かどうかは関係なくて、有言桁で演算しなきゃいけない、というところが効いているのだから。


    >「桁落ち」などのキーワードで検索すると良い情報が見つかるかも?
    数値計算的に言うなら、検索するキーワードは以下の四つ(他にあったかな?)。
    -桁落ち
    -桁あふれ
    -情報落ち
    -丸め誤差

    ちょっと場面は違うけど、極限を計算するときの内きりによる誤差というのもあります。
  • id:jwrekitan
    いや、私そういう規格とかの話はできませんもの。
    だから解答欄ではなくコメント欄に逃げてるわけでして^^;
  • id:a-kuma3
    最近、こういう話が人力検索ではあまりできないので、つい :-)

    質問者さんの書きっぷりだと、「自信がない」とのことなので、桁落ち、桁あふれなど、通り一遍のことは知ってての質問じゃないかな、と思って書いてます。
  • id:kyo_bracer
    皆様ありがとうございました。
    a-kuma3さんのご指摘の通り一通りの項目は知っている「つもり」ですが、体系的に学習したわけではないので抜けがあるのではないか、考え方が間違っていないかという感覚が拭えず、まとめて学習し直したいというのが質問させて頂いた主旨でした。

    ご紹介頂いたSun、DOBON.NETに加え「Write Great Code」の該当部分を読んで自分なりに検証しています。
    どうもありがとうございました。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません