木村です. > 鶴谷さん [FreeBSD-users-jp 91686] Re: long double の bug ? について
> おそらくは、 > http://gcc.gnu.org/bugzilla/show_bug.cgi?id=17778 > が原因でgccにこんなコードが挿入されたのだと思うのですが、 ここでは,fpsetprec(FP_PE); のことが一言も出てこないのが 奇妙です.ここに報告された問題はむしろそちらに関係しています. 従って,このテストコードをそのまま使うと,私の報告した定数の 問題と fpsetprec(FP_PE) の問題とが混在することになります. > しかし、よくわからない問題が残っています。 > 上述のバグレポートで提示されているテストコードをビルドした結果を見ると、 > ほかのOSでの結果と異なります。 > Debian : -m96bit-long-doubleなどのオプションがあっても無くても、 > 0xc.90fdaa22168c4p-2 > 0xc.90fdaa22168c4p-2 > FreeBSD : -m96bit-long-doubleなどのオプションがあっても無くても、 > 0xc.90fdaa22168c4p-2 > 0xc.90fdaa22168cp-2 うちの FreeBSD 6.2 + gcc 3.4.6 では 96, 128bit で違います. 96bit: c9 0f da a2 21 68 c0 00 : 0xc.90fdaa22168cp-2 <- 最初から精度が低い c9 0f da a2 21 68 c0 00 : 0xc.90fdaa22168cp-2 128bit: c9 0f da a2 21 68 c4 00 : 0xc.90fdaa22168c4p-2 c9 0f da a2 21 68 c0 00 : 0xc.90fdaa22168cp-2 <- 演算により精度が落ちる このコードに fpsetprec(FP_PE); を加えると,演算による 精度の低下はなくなります. 96bit: c9 0f da a2 21 68 c0 00 : 0xc.90fdaa22168cp-2 c9 0f da a2 21 68 c0 00 : 0xc.90fdaa22168cp-2 <- こちらは判別できない 128bit: c9 0f da a2 21 68 c4 00 : 0xc.90fdaa22168c4p-2 c9 0f da a2 21 68 c4 00 : 0xc.90fdaa22168c4p-2 それに加えて,d を定数の代入でなく sscanf() を使って 「初期化」すると,96, 128bit の差は無くなります. (数値が違うのは,読み込ませた数値が違うから.) c9 0f da a2 21 68 c2 35 : 0xc.90fdaa22168c235p-2 c9 0f da a2 21 68 c2 35 : 0xc.90fdaa22168c235p-2 ちなみに,上述の Web のテストコードを次のようにいじりました. printf() で数値を出すよりも,ana_ldouble() を使って中身を hexa で出した方が,比較検討が容易だと思います. #include <stdio.h> #include <ieeefp.h> /* function prototypes */ void ana_ldouble( long double x ); int main(void) { #if 1 /* 1 : original */ volatile long double d = 0x3.243f6a8885a31p0L; #else long double d; sscanf("3.14159265358979323846L","%Lf",&d); #endif #if 1 /* 0 : OFF, 1 : ON */ fpsetprec(FP_PE); #endif ana_ldouble(d); printf("%La\n", d); d = 2.0L * d / 2.0L; ana_ldouble(d); printf("%La\n", d); return(0); } void ana_ldouble( long double x ) /* for 80 bits */ { int i; unsigned int s; typedef union { long double l; unsigned char c[16]; } exd; exd e; for ( i = 0; i < 16; i++ ) e.c[i] = '\0'; /* clear */ e.l = x; s = sizeof(long double); for ( i = 7; i >= 0; i-- ) printf("%02x ",e.c[i]&0xff); printf(" : "); } > 試しに、FreeBSD 6.3で、ports/lang/gcc42に、 (中略) > -#define TARGET_96_ROUND_53_LONG_DOUBLE (!TARGET_64BIT) > +#define TARGET_96_ROUND_53_LONG_DOUBLE 0 > のようなパッチを当ててビルドして使ってみると、 > -m96bit-long-doubleと-m128bit-long-doubleの出力の違いはなくなりました。 大変な検討をしていただき申し訳ありません.それにしても, これだと意図的にやっている,すなわち「bug でなく仕様」なので すね.だとしても,対象が 96bit のみだとか,sscanf() で読み込ませ れば精度は落ちないとか,一貫性に欠ける仕様のように思えます. あと,Linux (Debian) や cygwin や Mac ではこれをやっていない わけで,FreeBSD だけこれをやる意味はないと思います. (合理的な説明が出てこない限りは.) Satoshi Kimura ([メールアドレス保護])