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

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

新規登録して質問してみよう
ただいま回答率
85.48%
アーキテクチャ

アーキテクチャとは、情報システム(ハードウェア、OS、アプリケーション、ネットワーク等)の設計方法、設計思想、設計思想に基づいて構築されたシステム構造をアーキテクチャと呼びます

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

Q&A

解決済

3回答

777閲覧

浮動小数点数についてです。

strike1217

総合スコア651

アーキテクチャ

アーキテクチャとは、情報システム(ハードウェア、OS、アプリケーション、ネットワーク等)の設計方法、設計思想、設計思想に基づいて構築されたシステム構造をアーキテクチャと呼びます

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

0グッド

0クリップ

投稿2017/06/19 13:56

編集2017/06/20 12:32

以前、浮動小数点数について質問したのですが、今回はフォーマットについてです。

double型のメモリ領域
面倒なので今回もdouble型に限定しますね。

#include<iostream> #include<math.h> int main(){ double x = 1.7320508075688774152212090484681539237499237060546875; int exp; double k = frexp(x, &exp); printf("%1.80lf = \n%1.80lf x 2 ^ %05d\n", x, k, exp); return 0; }
1.73205080756887741522120904846815392374992370605468750000000000000000000000000000 = 0.86602540378443870761060452423407696187496185302734375000000000000000000000000000 x 2 ^ 00001

この結果をよーーくみると変ではないでしょうか?
私の本のIEEE754の書式によりますと、以下のようになるはずです。

浮動小数点数 = (-1)^S x (1 + 仮数部) x 2^(指数-127)
つまり、最小は、1.0000 0000 0000 0000 0000 ・・・(2)x2 ^ (-1072)

仮数部の最小値は1以上になるはずです。
しかし上記のプログラムの仮数部はどうみても1から始まってないでよね?

この書式の違いは一体どういう事でしょうか?

詳しく理解できなかったのですが、先頭に1がある表記を正規化した科学記数法というそうです。
なので上記のプログラムは不正規化数ということになると思います。

不正規化数が演算の対象に出てきた場合には例外を発生させて、あとはソフトウェアで補うようにしているコンピュータが多い。

ちょっと、何が言いたいのかわからないのですが、frexp関数による書式がIEEE754のフォーマットに準拠していない理由は何でしょうか?非常に分かりにくいです。
混乱しています。
また歴史が絡む問題でしょうか?IEEE754が規定される前に作られた関数なのかな・・?と考えましたが・・・

2、誤差に関する確認事項です。
超簡単にするために、0.02568という数字を考えます。
何らかの演算の結果の仮数部の数字と考えて下さい。

この場合、
ガード桁:5
丸め桁:6
スティッキー・ビット:8

これで正しいですか?
スティッキービット = 丸め桁の右側に0でないビットがあることを示すものである。
つまり、double型の仮数部の52bitの最後の3桁に特別な名前を付けただけという事ですかね?

2番については間違いがあったらご指摘をお願いします。

「追記」

#include <iostream> #include <math.h> int main() { float value = 1.73214; int exp; float x = std::frexp(value, &exp); printf("%1.60lf = \n", value); printf("%1.60lf * 2^ %08d\n", x, exp); union { float f; int i; } a; a.f = value; printf("%f ( %08X )\n", a.f, a.i); /* ビットの列を表示します */ for (int i = 31; i >= 0; i--) { printf("%d", (a.i >> i) & 1); } printf("\n"); /* 指数部( 1ビット )、指数部( 8ビット )、仮数部( 23ビット )を取り出します */ printf("符号部 : %x\n", (a.i >> 31) & 1); printf("指数部 : %x\n", (a.i >> 23) & 0xFF); printf("仮数部 : %x\n", a.i & 0x7FFFFF); }

1.732139945030212402343750000000000000000000000000000000000000 =
0.866069972515106201171875000000000000000000000000000000000000 * 2^ 00000001
1.732140 ( 3FDDB6C3 )
00111111110111011011011011000011
符号部 : 0
指数部 : 7f
仮数部 : 5db6c3

結果はこのようになりました。

問題はここからで、
仮数部の16進数を10進数に直すと、6141635となります。
(指数部を10進数にすると127でした。)

frexp関数の仮数部とは大きく異なる値です。
そして、この6141635から0.866069972515106201171875にどうやって変換しているのかさっぱりです。

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

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

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

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

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

guest

回答3

0

追記にのみ回答

仮数部
0x_5db6c3は
二進表記で
0b_101 1101 1011 0110 1100 0011
IEEE754に基づくと、この値は(1+小数部分)の小数部分に当たるので
0b_1 + 0b_0.101 1101 1011 0110 1100 0011
= 0b_1.101 1101 1011 0110 1100 0011
= 0d_1.7321399450302124

指数部は127バイアスされているので127-127=0

そういうわけでちゃんと
1.7321399450302124 * 2^0になっています。

要約すると

仮数部の16進数を10進数に直すと、6141635となります。

が誤りで、ここは固定小数点数です。

投稿2017/06/20 14:40

ozwk

総合スコア13521

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

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

strike1217

2017/06/20 14:45

おお!! 変換の仕方に間違いがありましたか! すいません。 frexp関数は、仮数部を1.7321399450302124 から 0.866069972515106201171875に変換しているだけということですか?
ozwk

2017/06/20 14:51 編集

そうですね メモリにはIEEE754形式でちゃんと値:1.73~が格納されていて(当然) frexp()はその値(1.73~)から彼なりの浮動小数点表記を計算して仮数部0.866~ と 指数1を返しますよってことです。
strike1217

2017/06/20 14:59

あああ やっとわかりました。 frexp関数の仮数部を1以下にするように2で割る代わりに指数部を割るごとに増やせば、内部がIEEE754でも別の形式に変換できるわけですね!! ありがとうございます。
guest

0

ベストアンサー

EEE754が規定される前に作られた関数なのかな・・?と考えましたが・・・

その通り、歴史的理由です。
C言語がUnix上で発展してきた事はご存じでしょうが、初期のUnixは今は無きDEC(COMPAQが吸収合併さらにHPと合併)のPDP-11というコンピューター(ミニコン)上で動いていました。
PDP-11や後継のVAX-11が使っていた浮動小数点は、仮数部が0.5以上1未満になるように正規化されていました。
Unix上のCで扱う浮動小数点型の関数はそれをモデルにしていますので、その後に定められたIEEE754(仮数部が1以上2未満)とその点が合いません。

以前色々検索したのですが、PDP-11の浮動小数点フォーマットを記した日本語ページは見つかりませんでした。インターネット以前のIT界情報は少ないです。
私の記憶では、上記以外は、ビット構成など同じだったはずです。
IEEE754を知った時、「え?仮数部が1以上2未満なの?」とビックリした記憶があります。

#追記に対して

仮数部の16進数を10進数に直すと、6141635となります。

そこがそもそも間違っています。

0x5db6c3は、小数部ですが、23bitなので、省略された24bit目に整数部1を付けて、0xddb6c3 が仮数です。小数点は、先頭ビットの次にありますので2進数で表すと、0b1.101 1101 1011 0110 1100 0011 。指数は 0x7fから127を引いて0ですね。
仮数部を0.5以上にしないといけないので、仮数を2で割って、指数に1を足します。全体の値としては変わりません。仮数を2で割るというのは、2進数で小数点を1桁左へ移動させるということなので、0b0.1101 1101 1011 0110 1100 0011
仮数を10進数に直すと、0.5+0.25+0.0625+(以下略) = 0.8660699~
これに2の1乗を掛けた物が、元の数値になります。

投稿2017/06/20 00:39

編集2017/06/20 15:51
otn

総合スコア84533

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

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

strike1217

2017/06/20 01:30

なるほど! そうなんですか!
strike1217

2017/06/20 01:34

frexp関数通りにビット列がメモリ上に格納されている訳ではないんですよね? 0.86602540378443870761060452423407696187496185302734375000000000000000000000000000 これがメモリ上に入っていた場合は、 +1されるので、1.866025403784438... と解釈されてしまいますよね?
otn

2017/06/20 01:43

> frexp関数通りにビット列がメモリ上に格納されている訳ではないんですよね? メモリ上は、「そのCPUの扱う浮動小数点フォーマット」でしょう。 x86だとIEEE754ですね。 > +1されるので、1.866025403784438... と解釈されてしまいますよね? +1とは何の事ですか?
strike1217

2017/06/20 01:46

浮動小数点数 = (-1)^S x (1 + 仮数部) x 2^(指数-127) IEEE754の仮数部の「+1」のことです! frexp関数で内部を覗こうとしてもx86CPUだとあてにならないということですね! あまり使えない関数ですね・・・
otn

2017/06/20 03:19

内部を覗くなら、メモリを直接見ないと駄目です。 printf("%016X\n", x); fexpが返すのはメモリに格納されている内容とは異なり論理的な値です。これはPDP-11でも同じ。
strike1217

2017/06/20 05:07

浮動小数点数 = (-1)^S x (1 + 仮数部) x 2^(指数-127) とは、メモリの中の2進数のビットを10進数に直す方法の1つという事ですか?
otn

2017/06/20 06:51

10進数というか、表す数値に直す方法です。2進数か10進数かはそれをどう表現するかで決まります。 「方法の1つ」とお書きですが、方法は形式ごとに唯一です。 お書きの物は、IEEE_754単精度の場合ですね。倍精度だと違いますし、PDP-11形式でも違います。
strike1217

2017/06/20 07:15

IEEE_754形式でもPDP-11形式でもメモリの内容は同じなんですか? 表現の仕方か異なるという事ですよね?
otn

2017/06/20 07:24

> IEEE_754形式でもPDP-11形式でもメモリの内容は同じなんですか? 違いますよ。「数値をメモリに格納する形式に変換する方法」「メモリ内容を数値に変換する方法」が、形式ごとに違います。IEEE_754でも形式は何通りもあります。 方法が違えばメモリ内容も違う。
strike1217

2017/06/20 07:30

IEEE754形式のx86cpuでなぜPDP-11形式であるfrexp関数が使用可能なのでしょうか? 無理やり変換しているんですかね?
otn

2017/06/20 07:43

> IEEE754形式のx86cpuでなぜPDP-11形式であるfrexp関数が使用可能なのでしょうか? 別にfrexpはPDP-11形式というわけではありません。 「仮数部が、0.5以上1未満」という仕様がPDP-11などに由来するということだけです。
otn

2017/06/20 08:27

誤解のポイントが判ったような気がします。 frexp関数が内部形式を返す関数だと思っているのですね。違いますよ。
strike1217

2017/06/20 08:45

「frexp関数が内部形式を返す関数だと思っているのですね。」 そうです!! 違うんですか?
strike1217

2017/06/20 08:52

frexp関数とは昔の形式に直すだけですか? ちょっとよくわからないですね。
otn

2017/06/20 09:17

細かいところを省くと、浮動小数点数を、『「0.5以上1未満の数」×2の「整数」乗』に変換して返す関数です。 内部形式とたまたま一致する場合もあるし、一致しない場合もある。 細かいところ・・・符号、ゼロの場合、無限大の場合、非数の場合
strike1217

2017/06/20 09:30

あ、やっと分かりました。 x86_cpuはIEEE 754に則っているが、frexp関数を使用すると昔の形式に由来されているので昔の形式で表されるわけですね!
strike1217

2017/06/20 09:50

ベストアンサーにかなり迷いましたが、させてもらいますね。 chironianさんの方にも+1しておきました。
strike1217

2017/06/20 11:16

・・・しかしよく考えると、まだピンと来ないですね・・・ 内部表現がIEEE 754なのにどうして『「0.5以上1未満の数」×2の「整数」乗』の形式に変換できるのかが分からないですね・・・・??
otn

2017/06/20 11:38

どんな形式でも変換できますよ。なぜできないと思うのか不思議でなりません。
strike1217

2017/06/20 11:59

そうなんですか! ・・・ メモリ内の2進数がIEEE 754に則っているなら、異なる形式に変換すると別の数値になってしまうんじゃないかと思うんですが・・・
otn

2017/06/20 12:19

すいませんが、どこで詰まってらっしゃるのわからないので、説明できません。
strike1217

2017/06/20 12:26

追記いたしますね。少々お待ちください。
strike1217

2017/06/20 22:22

やっと理解できました。 お手数をかけました。
guest

0

こんにちは。

仮数部の最小値は1以上になるはずです。

IEEE754ではその通りですね。

しかし上記のプログラムの仮数部はどうみても1から始まってないでよね?

frexp関数が返却する値がIEEE754で決められている仮数部をそのまま返却しなければならない理由は特にないと思います。
IEEE754で定められた仮数部を2で割った値を仮数部とし、指数部を+1しても大した問題はないと思います。

そして、正規化しない場合、同じ値浮動小数点値に対して、仮数部と指数部の組み合わせが複数発生し、取り扱いが非常に面倒です。IEEE754やfrexp関数は正規化しています。

IEEE754は0でない場合、最上位ビットを小数点の左隣に定めました。
frexp関数は、0でない場合、最上位ビットを小数点の右隣に定めました。[1/2, 1)は、0.5以上1未満です。これは小数点の右隣がちょうど1になるな範囲です。
つまり、正規化方法が異なるだけです。

もしも、frexp関数の非0の時の仮数部を[1, 2)と定めていればIEEE754の正規化と一致しますが、恐らくfrep関数の仕様の方が先に決っているはずです。
あ、上記のリンク先の最後にそのように書かれてました。

Why does frexp() not yield scientific notation?

frexp()が戻り値を[1, 2)の範囲ではなく、[0.5, 1)の範囲に収めるようにしている理由は、IEEE 754およびISO/IEC 60559が策定される前に作られた関数であることが理由と考えられる

次の質問ですが、

ガード桁:5

丸め桁:6
スティッキー・ビット:8

Guard Bit, Round Bit, Sticky Bitの意味なら、全く違うようです。

仮数部の下に,ガード ビット(Guard bit),ラウンド ビット(Round Bit),スティッキービット(Sticky Bit)の3つのビットが用意されている.これら3つのビットの値はFPUが演算中に自動でセットするもので,ユーザーが値をとりだして演算に利用することはできない(できなくなっているのが普通).

仮数部が53ビットの時、更にその下の3ビットのことですね。
仮数部の先頭ビットを第0ビットとした時、第53, 54, 55ビットです。
FPUが最近値丸め(Round to Nearest)処理を行うためのFPUの内部処理用のビットということです。FPUを設計するのでないかぎり、気にする必要はないだろうと思います。

投稿2017/06/19 15:45

編集2017/06/19 15:49
Chironian

総合スコア23272

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

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

strike1217

2017/06/20 01:37

「IEEE754やfrexp関数は正規化しています。 」 正規化の方法が相対的に異なるのですね! 厄介ですね。 「仮数部の先頭ビットを第0ビットとした時、第53, 54, 55ビットです。」 そうなんですか! 分かりました。
strike1217

2017/06/20 01:42

浮動小数点数の仮数部が53bitとなっているのは、ユーザー側からみた場合ということですよね? メモリ上では、55bitまでの領域が存在していることになるのでしょうか?
Chironian

2017/06/20 05:43

普通にFPUを使う際には、スティッキービット等の追加の3ビットはFPUの内部にのみ存在します。主記憶には記録されません。 例えばCPUが足し算処理する際にキャリービットを使いますね。 そのキャリービットに近い意味合いです。CPU内部にのみ存在し、主記憶には記録されません。(キャリーはプログラムから見れますが、スティッキービット等は見れないようですけどね。)
strike1217

2017/06/20 05:53

なるほど!! わかりました。
strike1217

2017/06/20 13:46

追記いたしました。
strike1217

2017/06/20 14:34

float型でやったのですが、 1.732140 1.6141635(IEEE 754形式) かなり値が異なるようです・・・ 誤差が大きすぎませんか?
Chironian

2017/06/20 15:06

> 指数部 : 7f > 仮数部 : 5db6c3 これは1.73213994503021240234375をIEEEE754形式で表現した時の指数部と仮数部の内部表現を16進数で表したものですね? 元の値は1.73213994503021240234375ですから、誤差は全くないようです。(当たり前ですが。) 指数部は127の下駄履き表現です。0x7f=127ですから、0x7f-127=0なので、指数の値は0です。 指数の値が0ですから、仮数部の0x5db6c3はちょうど小数点以下のものです。それに1を加えると元の値になります。 16進数で小数点以下の計算は難しいので2進数(00111111110111011011011011000011)から、変換してみて下さい。同じ値になることを確認できると思います。 2進数で0.1は1/2です。0.01は1/4です。0.001は1/8です。以下同文。 小数点下第i桁のビットが1ならば、その値は1/(2^i)です。 1であるビットの値を全て足せば元の値を回復できます。
strike1217

2017/06/20 22:22

やっと理解できました。 お手数をかけました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問