質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

4回答

3193閲覧

C言語,x87の浮動小数点計算の精度について

Gustave

総合スコア21

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

2クリップ

投稿2015/07/03 10:53

x87系の浮動小数点の精度について

開発環境はC言語で,CentOS 64bit,gcc,Sandy Bridge世代のcore i7です.
Cで誤差の検証をしていて,倍精度を使って内積を計算していました.

計算した後,内積の結果aを,
printf("%.24e",a);
などとすると,24桁まで何か数字が入って出力されます.一応,いろんな値で試しました.
(それ以上何桁表示されるのかは未テスト,コンパイラの最適化のオプションなどで変化はない)
倍精度は15-16桁しか精度はないはずですが,残る8桁(24-16)として出力される値はなんなのでしょうか.

メモリ空間上にある後ろのゴミかと思って,aを配列で確保して,a[1]に0や値を入れたりしてみましたが,結果は変わらずです.
もしやメモリ空間上でも計算の中間結果の80bitとして値を確保しているのでしょうか?

もちろん,信頼区間は16桁なのでそれ以下は信頼しないべきしょうが,アーキテクチャの興味として,
・17桁目以降もあっている可能性があるのか?(一応,何らかの計算をして得た値なのか,ゴミなのか)
・17桁以降の値はなにか?メモリ空間として何ビット持っているのか?
が気になっています.

どなたかご教授ください.

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ベストアンサー

10進数に直したからだと思います。
2の52乗分の1は、
0.0000000000000002220446049250313080847263336181640625
なので、それくらいの桁にはなります。

1.0より最下位桁分だけ大きい数値を出力してみます。

lang

1#include <stdio.h> 2main(){ 3 double x; 4 char *p; 5 p=(char*)&x; 6 p[0]=1; // 最下位bitだけ1 7 p[1]=0; 8 p[2]=0; 9 p[3]=0; 10 p[4]=0; 11 p[5]=0; 12 p[6]=0b11110000; 13 p[7]=0b00111111; // 符号=0、指数=1023 14 printf("%.60e\n",x); 15} 16// => 1.000000000000000222044604925031308084726333618164062500000000e+00

投稿2015/07/03 12:01

編集2015/07/03 21:54
otn

総合スコア84380

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

直接的な回答ではありませんが、一応・・。

質問者さんは64bitのCentOSを使っているとおっしゃってますから、まずは本当にx87が使われているのか確認されたほうがいいと思います。x86_64(AMD64)と名乗るにはSSEが実装されてる必要があります。gccはx87の浮動小数点レジスタではなくSSEの浮動小数点レジスタを使うかもしれません。例えば私の64bitのLinux環境では何もオプションに指定しなければ内部精度128bitのxmmレジスタを使ったコードを出力しました。すべての浮動小数点演算をx87で行うには-mfpmath=387オプションをつける必要があります。

そのうえでディスアセンブルして、メモリにストアするfstp命令を見つけてオペコードの先頭が0xddなのか0xdbなのかチェックして、どっちの精度で渡されてるのか確認します。それが0xddなら、「メモリ空間上でも計算の中間結果は80bitとして値を確保しているのでしょうか?」という質問には「私の環境ではNOです」と答えることができます。もし0xdbだったら・・そんなことは無いと願ってます。x86_64はアライメントに厳しいですし、C言語に80bit浮動小数点みたいな型は無いですから。

$ cat a.c #include <stdio.h> int main(int argc, char *argv[]) { double d = 0.111; printf("%.24e\n", d); return 0; } $ gcc -mfpmath=387 a.c $ ./a.out 1.110000000000000014432899e-01 $ objdump -D a.out | grep "<main>" -A 20 0000000000400506 <main>: 400506: 55 push %rbp 400507: 48 89 e5 mov %rsp,%rbp 40050a: 48 83 ec 30 sub $0x30,%rsp 40050e: 89 7d ec mov %edi,-0x14(%rbp) 400511: 48 89 75 e0 mov %rsi,-0x20(%rbp) 400515: dd 05 c5 00 00 00 fldl 0xc5(%rip) # 4005e0 <_IO_stdin_used+0x10> 40051b: dd 5d f8 fstpl -0x8(%rbp) 40051e: 48 8b 45 f8 mov -0x8(%rbp),%rax 400522: 48 89 45 d8 mov %rax,-0x28(%rbp) 400526: f2 0f 10 45 d8 movsd -0x28(%rbp),%xmm0 40052b: bf d8 05 40 00 mov $0x4005d8,%edi 400530: b8 01 00 00 00 mov $0x1,%eax 400535: e8 a6 fe ff ff callq 4003e0 <printf@plt> 40053a: b8 00 00 00 00 mov $0x0,%eax 40053f: c9 leaveq 400540: c3 retq

その他の2つの質問についてはotnさんが回答してくださってます。

投稿2015/07/04 13:28

sharow

総合スコア1149

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

・17桁目以降もあっている可能性があるのか?(一応,何らかの計算をして得た値なのか,ゴミなのか)
・17桁以降の値はなにか?

コンピュータ内部は、double型(2進表現)で値を保持してます。
んで、printf文で出力しているのは、これを10進表現した値です。

doubleの仮数部で表現可能な10進表記の範囲は限られるので、
有効桁以降は精度不足となり、値を保障出来ません。

なので、17桁以降でも有効桁付近であれば、足りない精度で頑張って
10進表現した結果、たまたま合っている可能性も一応あります。

・メモリ空間として何ビット持っているのか?

sizeof(double)でバイトサイズを求めれば、確認出来るかと。
※long doubleでなく通常のdoubleなら普通は64bitですね。

投稿2015/07/04 12:21

horohoro

総合スコア490

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

x87 の 倍精度浮動小数点は、10バイトダブルと言われる特殊なやつじゃなかったかな。
long double 使ってるのでしょ?
https://en.wikipedia.org/wiki/Long_double
普通の 64bit のダブルは、有効桁数 14-15 桁程度、小数点以下だけなら結構表わせたはず。
long double は16 * 2 の専用バンクレジスタ使ったどーたらこーたらって記憶があるな。
8 * 4 だったかも。ちょい忘れました。確か、3*3の行列を1クロックで実行できるように
複数バンクの同時実行機能を備えていたような気がします。

10バイトダブルは、パック10進数(1バイトで2桁表現する)ですね。なので、有効桁数は確か 18桁くらいじゃなかったかと。
80ビット(10バイト)は、バンクレジスタに格納されています。CPUの汎用レジスタには10バイト
を格納するレジスタはないので、
足し算するときは、
1+2
は、
バンク0-0に1の80bit 表現値、
バンク0-1に2の80bit 表現値、
CoPro の専用加算命令で加算結果を、メモリに直接ロードしてたようなかすかな記憶があります。
そういうのを解説したソフトバンク製の日本語の資料を持っていたんですが、知人にあげちゃいまして・・・

投稿2015/07/03 18:48

ipadcaron

総合スコア1693

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ipadcaron

2015/07/03 18:57

メモリ上に10バイト持っているのか?という質問でしたね。 メモリto バンク、バンクtoメモリのデータロード専用命令があるのでメモリには存在するはずですよ。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問