木村です.

  下記のようなトラブルに遭遇しました.調べた限りではこの問題は
報告されていないので,この ML で情報を募りたくメール致しました.

1. 状況
  FreeBSD 5.3 + gcc(3.4.2) で作成した,拡張倍精度 (long double)
を使ったプログラムを FreeBSD 6.2 + gcc(3.4.6) にて使用すると,
結果が違いました.検討した末,以下のことが分かりました.

  gcc に -m96bit-long-double (default) と -m128bit-long-double の
option を付けた場合を各々 96bit, 128bit と書くことにしますと,
(1) FreeBSD 5.3 (96, 128bit), FreeBSD 6.2 (128bit) の三者の
  結果は同じになる.
(2) FreeBSD 6.2 (96bit) の場合,(1) よりも概して精度が悪い.

2. 原因
  この問題の生じ方を探りますと,「FreeBSD 6.2 において拡張倍
精度として 96bit を使うと,式の中の定数の精度が double に
落とされてしまう」ことに起因していることが分かりました.
(この段階では「原因」とは言えないでしょうが.)

  例えば,こんな使い方をした場合です.
   x = 3.14159265358979323846L;
   x = sqrtl(1.5L);

参考までに,この問題の簡単な確認法を示します.

main(void)
{
   double c;
   long double lc;

   c  = 3.14159265358979323846;
   lc = 3.14159265358979323846L;
}

これを s.c とし,二通りにコンパイルしてアセンブラを出力します.

% gcc -m96bit-long-double  -S s.c -o  s1.a
% gcc -m128bit-long-double -S s.c -o  s2.a

両者のアセンブラの,この数値に関連した部分は次のようになります.
(ちなみに,FreeBSD 5.3 の二つと,FreeBSD 6.2 にて -m128bit-long-double
 を規定した場合の三者は同じです.)

変数 c
        .long   1413754136         ← 仮数部 (2)
        .long   1074340347         ← 符合,指数部,仮数部 (1) が混在

変数 lc
        movl    $560513024, %eax   ← 仮数部 (2). 128bit では $560513589
        movl    $-921707870, %edx  ← 仮数部 (1)
        movl    $16384, %ecx       ← 符合と指数部

この数値から double の変数 c の仮数部 (mant) を求めると次のようになります.

double : 1074340347 1413754136   mant = 09 21 fb 54 44 2d 18

これを long double の変数 lc の仮数部と比較するために,
左に 3bit シフトして,かつ最上位の 1bit を立てます.
それと lc の 96, 128bit の場合の仮数部を並記すると次のようになります.

double :                        mant = c9 0f da a2 21 68 c0
 96bit : -921707870 560513024   mant = c9 0f da a2 21 68 c0 00
128bit : -921707870 560513589   mant = c9 0f da a2 21 68 c2 35

すなわち,96bit の拡張倍精度の場合,定数の精度が倍精度に落とされ
てしまっています.

3. お願い
  以下の二点を確認して,必要ならば send-pr しようと思います.
(1) これは bug なのか,隠れ compile option などがあるのか?
(2) bug であったとして,FreeBSD 6.3 や 7.0 でもこの問題は起き
  続けているのか? (これらはうちにないので確認できません.)

先ず,(1) について御存じの方,情報をお願い致します.

                     Satoshi Kimura  
([メールアドレス保護])

メールによる返信