- ベストアンサー
float型での最小値について調査
- 「float型」での最小値について調べています。実際の値と「float.h」の定義との違いについて解説します。
- 「float.h」の中にある「FLT_MIN」は、最小の正の値を表す定数です。実際の値は「1.175494351e-38F」となっています。
- 使用する際には、「FLT_MIN_EXP」を使って指数部分を調整する必要があります。計算の際には注意が必要です。
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
No1,3です。 2進の浮動小数点表現は、ふた通りあって、 (1) 仮数部を1以上2未満に正規化する方法・・・IEEE754という標準規格で定められた方法 現在2進浮動小数点表現のほとんどすべてのCPU(多分事実上すべて)はこちら (2) 仮数部を0.5以上1未満に正規化する方法・・・PDPという昔のミニコンピュータで使われていた方法 UnixやCは当初PDP上で開発された Cのマクロ定数類や、frexp()関数は、上記の歴史的経緯(過去との互換性)のため(2)をベースにしています。規格書の後の方のmath.hのfrexp関数の説明に仮数部は区間[1/2,1)と書いててあります。 今回、規格書の前の方の16ページも見てみましたが、「浮動小数点数を次のモデルによって定義する」の部分の式はk=1から始まっており、f1>0となるように正規化すると書いてあるので、2の-1乗の位(つまり0.5の位)が1となるように正規化する、つまり、上記の(2)をベースにしていることがここからもわかります。 で、実際のCPUは(1)で動いているわけですが、 (参考: http://ja.wikipedia.org/wiki/%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0 ) ここでは、指数部は8ビットで、0000 0000~1111 1111 に対応するのが-127するので、指数の範囲は、-127 ~ 128 です。 ただし、128(1111 1111に相当)の場合は、非数や無限大を表すので、実際に使える最大指数は127になります。 最小は-127ですが、このとき仮数部が最小のオールゼロだと、指数部・仮数部ともにビットパターンがゼロになって、これも実数値のゼロを表すので使えません。つまり最小の指数は-126になります。 で、以上は仮数部が1以上2未満の(1)の世界での話なので、仮数部が0.5以上1未満の(2)の世界で表現し直すと、1つずれて最大が128、最小が-125になります。これがマクロの値です。 ( 1 = 1 x 2^0 = 0.5 x 2^1 と、指数が一つずれる )
その他の回答 (5)
- akayoroshi
- ベストアンサー率50% (46/91)
> JIS規格でのFLT_MAXを求める計算式の(1-b^-p)というのは1未満(暗黙の1を抜いた仮数部)を求めているのではないでしょうか? bが2のときは、(1-b^-p)を2倍した値が仮数部の最大値になり、これから1を引いた値が<暗黙の1を抜いた仮数部>になります。(1-b^-p)は2進でp桁の1が並ぶ数値になりますが、これは<暗黙の1を抜いた仮数部>の桁数より1桁多くなります。 > そしてその値をFLT_MAX乗しているのだと思います。 その値にb^emaxを掛けています。 私がNo.5で回答した「2.0未満の最大の実数値」は、「(1-b^-p)を2倍した値」と等しく、これにb^(emax-1)を掛けるのは、2倍する前の(1-b^-p)にb^emaxを掛けるのと等しいことになります。
お礼
私の書き方が曖昧、かつ間違っているために混乱させてしまい申し訳ありません...。 (返信も遅くてすいません..うう) 私がNo5のお礼で書いた「1未満」という部分は「2^-1以下の合計値」のつもりでした。 そして「そしてその値をFLT_MAX乗しているのだと思います。」は書き間違いでした。 「2^FLT_MAX乗」が正しいです。 JIS規格に書いてある (1-b^-p) * b^emax というのは、 「2^-1から2^-23までの部分をemaxぶんだけ左シフトする」 と書いてあるのだと考えています。
- akayoroshi
- ベストアンサー率50% (46/91)
JIS規格を抜粋してみます。手元にあったのはC言語のちょっと古い版(JIS X3010-1993)ですが、この項は変わっていないでしょう。 ***** 5.2.4.2.2 浮動小数点型の特性<float.h> ●指数の表現における基数 FLT_RADIX ●FLT_RADIXをその値から1減算した値でべき乗したとき、正規化された浮動小数点数となる最小の負の整数 FLT_MIN_EXP ●FLT_RADIXをその値から1減算した値でべき乗したとき、表現可能な有限の浮動小数点数となる最大の整数 FLT_MAX_EXP ***** FLT_RADIXは2なので、仮数の範囲は1.0以上2.0未満ということになり、 絶対値最小の数値は 1.0x2^(FLT_MIN_EXP-1) 最大の数値は (2.0未満の最大の実数値)x2^(FLT_MAX_EXP-1) となります。(No.4の回答後、最大値についての余計な節介の中で数値に(2.0未満の最大の実数値)を掛け合わせることを忘れたことに気付きました。)
お礼
akayoroshiさん、本当にありがとうございます!!浮動小数点数について考えるきっかけが出来て嬉しいです。 返信が遅れてしまい、申し訳ありません。(私は考えるスピードがとても遅いです...。) 私も「日本工業標準調査会」のホームページにて規格名称「プログラム言語C」というのを調べてみました。 PDFの「プログラム言語C」の16ページから20ページには、FLT_MAXやFLT_MINの計算式なども書いてありました。 実際にその計算式を計算してみると、float.hにあるFLT_MINやFLT_MAXの値がちゃんと出てきました。 ****「プログラム言語C」より*********************************************** ■float型の、最小の正規化された正の浮動小数点数(FLT_MIN)の計算式 b^(emin-1) emin FLT_RADIXをその値から1減算した値でべき乗したとき、正規化された浮動小数点数となる最小の負の整数(FLT_MIN_EXP) ■float型の、表現可能な最大の有限浮動小数点数(FLT_MAX)の計算式 (1-b^-p) * b^emax b 指数表現における基数(FLT_RADIX) p 浮動小数点数の有効数字部(FLT_RADIXを基数とする。)のけた数(FLT_MANT_DIG) emax FLT_RADIXをその値から1減算した値でべき乗したとき、表現可能な有限の浮動小数点数となる最大の整数(FLT_MAX_EXP) *************************************************************************** >最大の数値は (2.0未満の最大の実数値)x2^(FLT_MAX_EXP-1) 非常に細かいことなのかもしれないのですが、JIS規格でのFLT_MAXを求める計算式の(1-b^-p)というのは1未満(暗黙の1を抜いた仮数部)を求めているのではないでしょうか? そしてその値をFLT_MAX乗しているのだと思います。
- akayoroshi
- ベストアンサー率50% (46/91)
float型の正規化数の絶対値の最小値は 2のFLT_MIN_EXP乗 ではなくて、2の(FLT_MIN_EXP-1)乗 です。2の-126乗が正規化数の最小値です。 (float型数値の絶対値の最大値も 2のFLT_MAX_EXP乗 ではなくて、2の(FLT_MAX_EXP-1)乗 です。) なお、正規化数の仮数部の値の範囲は、1.0以上2.0未満です。
お礼
akayoroshiさん、ありがとうございました。 (遅いタイミングのお礼となってしまい、すいません)
補足
akayoroshiさん、ご回答、ありがとうございます。 >float型の正規化数の絶対値の最小値は 2のFLT_MIN_EXP乗 ではなくて、2の(FLT_MIN_EXP-1)乗 です。2の-126乗が正規化数の最小値です。 CPUやFPU内部などでは仮数部を 1.0以上2.0未満(暗黙の1を含む)として考えるとOKなのかもしれないのですが、 C++言語上では、仮数部を 0.5以上1.0未満として考えると、私のやりたかったことが出来ました。(float.hに書いてある「FLT_MIN_EXP」や「FLT_MAX_EXP」を使って「FLT_MIN」や「FLT_MAX」を求めること) float型の最小値(float.hの「FLT_MIN」)を求めるために、 1.0 * 2^-126 ではなくて、 0.5 * 2-^-125 として考えると、float.hの「FLT_MIN」が出てきました。 (結果的に、やっていることは同じなのですが...) >(float型数値の絶対値の最大値も 2のFLT_MAX_EXP乗 ではなくて、2の(FLT_MAX_EXP-1)乗 です。) float型の最大値を float.hの「FLT_MAX(3.402823466e+38F)」として考えると「2の(FLT_MAX_EXP-1)乗」では「FLT_MAX(3.402823466e+38F)」とは違う値(1.701412e+038)が出てきます。
- notnot
- ベストアンサー率47% (4900/10358)
#1です。 すいません。質問がちゃんと読めてなかったみたいです。powを使った時の誤差の話じゃなくて、-125か-126かというのが質問ですね。勘違いしてました。 仮数部は0.5以上1未満なので最小の場合は0.5、指数部の最小が-125です。 なので、0.5 x 2^(-125) になります。 man frexp 参照。 CPUの扱う浮動小数点形式(レジスタ上とか)はまた違う形式になります。上記はC言語から見た場合。
お礼
notnotさん、ありがとうございました。 (お礼が遅れてしまい申し訳ないです)
補足
notnotさん、ご回答ありがとうございました!!。 float.hに書いてあるfloat型の最小値「FLT_MIN」を、float.hの「FLT_MIN_EXP」を使って求めることが出来ました。 また、最大値についてもまさしく float.h にある「FLT_MAX」を求めることが出来ました。 >仮数部は0.5以上1未満なので >CPUの扱う浮動小数点形式(レジスタ上とか)はまた違う形式になります。 >上記はC言語から見た場合。 恥ずかしながら、C言語上やC++言語上では「仮数部を0.5以上1未満として扱う」ことを知りませんでした。 仮数部を0.5以上1未満として考えれば、float.hに出ている「FLT_MAX」、「FLT_MIN」などを float.hの「FLT_MIN_EXP」、「FLT_MAX_EXP」を使ってほぼ同じ値を計算で出すことが出来ました。 「FLT_MIN」が-126ではなくて、-125なのは、C言語上では仮数部を0.5以上1未満(すでに一回、右シフトしている。これは暗黙の1を暗黙じゃなくしているのでしょうか?)としてみるからだったのですね(た、たぶん..)。 また、最大値「FLT_MAX」は2^-1のビットを仮数部の「暗黙の1」としてみて、そこから下の23ビットぶん( 2^-2 ~ 2^-24 )、つまり合計24ビット分を仮数部として、その値に 2^128 をかけることで求めることができました。 ありがとうございました!。
- notnot
- ベストアンサー率47% (4900/10358)
>2^-125 >とするのは間違っているのでしょうか? 合ってますが、プログラム作成方針が間違ってます。 powがどういう関数か知ってますか?pow(x,y) = exp(ln(x)*y) と、対数と指数関数を使ってますので、計算誤差が累積します。 下記で計算してください。 #include <stdio.h> main(){ double x; int i; x=1.0/2.0; for(i=0;i<125;i++) x /= 2.0; printf("%14.9e\n",x); }
補足
ご回答、ありがとうございました。 教えていただいたプログラムについてなのですが、 x=1.0/2.0; for(i=0;i<125;i++) x /= 2.0; 上記の部分で、1.0/2.0を合計で126回行っていると思います。 ということは、結果的に 2^-126 を計算していることになるような気がするのですが、、。 私の勘違いだったならば、申し訳ありません。
お礼
notnotさん、何度もすいません。大変に丁寧な説明をして頂き、ありがとうございます!!! 歴史的な経緯のお話が物凄くためになり、とてもおもしろかったです。 float.hに書かれている値の背景や、なぜ「仮数部が0.5以上1未満」となっているのかの理由がわかってすっきりしました。 質問をさせていただいた時には知らなかったことを知ることが出来て、うれしいです!