• ベストアンサー

Javaと倍精度浮動小数点数表現について

0.1のビット表現は 0-01111111011-1001100110011001100110011001100110011001100110011011 となるはずだと思うのですが、 Javaで簡単なプログラムを作って確認したところ 0-01111111011-1001100110011001100110011001100110011001100110011010 となります。なぜでしょうか? 仮数部の最下位ビットのことなので誤差なのかもしれませんが、そうだとすると10進数表現に戻したときではなく仮数の表現の時点で桁落ちしていることになりどうも腑に落ちません。 Double.doubleToLongBitsの実装ものぞいたのですが、C言語で記述されており慣れないためどうおってよいかわからず断念してしまいました。具体的には、 jdk-1_5_0-src-scsl\j2se\src\share\native\java\lang\Double.cというソース中の Java_java_lang_Double_doubleToLongBits(JNIEnv *env, jclass unused, jdouble v) という関数のなかの jdouble_to_jlong_bits(&v); 部分で変換しているのだと思うのですが jdk-1_5_0-src-scsl\j2se\src\windows\native\common\jlong_md.hというソース中の #define jdouble_to_jlong_bits(a) で止ってしまいそれ以降どこで定義されているのかわからなくなってしまいました。 これ以降どうおったらいいのか、どこに定義されているのか教えていただきたいです。 質問は2点です。 (1)0.1のビット表現がJavaでは予想と異なる理由・原因 (2)J2SEの実装ソースの追い方、具体的にDouble.doubleToLongBitsの処理がどのファイルに記述されているのか ご教授よろしくお願いします。 字数制限のためソースは一部のみはっておきます。 l = Double.doubleToLongBits(0.1); String tmp = ""; for (int i = 0; i < 64; i++) { if (i == 63 || i == 52) { tmp = "-" + tmp; } tmp = (l % 2) + tmp; l = l >> 1; }

質問者が選んだベストアンサー

  • ベストアンサー
  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.2

Javaの該当のメソッドの実装の詳細は知りませんが、十進数の 0.1 を二進に変換したときの ビットパターンは 000011001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 10 です(71桁目まで)。で、問題のdooubleで表現したときの最下位ビット 付近に注目すると 1001 (ここまで) 1001 ... となります。 多分、ここで丸める次のビットを0捨1入しているのでしょう。 つまり 1001 1 ↓ 繰り上がり 1010 であると。 質問者さんが 1011 だとおっしゃる根拠はわかりませんけど。 ビットパターンは次の Rubyスクリプトで求めました。 def d2b(v) r = "" 0.upto(70) do |i| s = if v>=10 then v-=10; "1" else "0" end v = v * 2 r << s end puts r end d2b(1)

arakororin
質問者

お礼

書き忘れましたがとりあえず、IEEE 754で定められている「ダブルフォーマット (double format)」ビットレイアウトを前提としていました。 >質問者さんが 1011 だとおっしゃる根拠はわかりませんけど。 あ。うそです。間違えました。 0-01111111011-1001100110011001100110011001100110011001100110011001 の書き間違いです。 あー、そこで私が誤認していただけなのですね。 >多分、ここで丸める次のビットを0捨1入しているのでしょう。 はい納得です。すっきりしました。 大変参考になりました。 どうもありがとうございました。

その他の回答 (1)

noname#49664
noname#49664
回答No.1

0.1は、表現を変えれば 1/16 + 1/32 + 1/256 + 1/512 + ・・・・ という無限級数となるわけで、2進数にすれば0.0001100110011001・・・という循環小数となり、決められた桁数では表現できないわけです。したがって、 >0-01111111011-1001100110011001100110011001100110011001100110011011 これも、実は間違ってるわけですね。正確に表現するためには無限の桁が必要なのですから、桁数を切られた段階ですべて「間違い(不正確)」といえばそうなります。「10進数表現に戻したときではなく仮数の表現の時点で桁落ちしていることになりどうも腑に落ちません」ということですが、0.1を2進数にする段階で必ずどこかで丸めてそれ以降の桁を切り捨てなければなりません。そもそも、限られた桁では絶対に正確な表現はできないのですから。  doubleの桁数範囲を超えて更にその先の桁があったとして考えてみると、 0-01111111011-1001100110011001100110011001100110011001100110011001 1001・・・ ↓ 0-01111111011-1001100110011001100110011001100110011001100110011010 なるほど、という感じはしますね。末尾が1010となるのは、そういう丸め方も考えられるわけで、別におかしいという感じはしないと思いませんか?  要するに「どの段階でどういう手法によって丸められるか」という問題なのだと思います。丸めの方法によって仮数の末尾部分がどうなるかは変化しますから。  Sunのドキュメントを検索すると、こんなのがありました。長文なんで私はほとんど目を通してませんが、「正確な丸め操作」あたりが参考になるのではと思います。 http://docs.sun.com/source/806-4847/ncg_goldberg.html ちなみに、Double.doubleToLongBitsはネイティブメソッドで、jlong_md.hはヘッダーファイル、実装はjlong_md.cというファイルになります。が、ソースコードのアーカイブには含まれていないようですね。検索しても見つからないので、あるいは公開されていないのかも知れません。(正確なことは私もわかりません。知っている方がいたらお願いします)

arakororin
質問者

お礼

最初は#1さんが何を言っているのか分かりませんでした。 でも結局#2さんと同じことを言っていたのですよね。タブン。 結局今回一番重要だったのは、私の予想値が間違っていたことでそこを指摘していただけたかどうかで評価しました。 参考になりました。ありがとうございました。

関連するQ&A

専門家に質問してみよう