• ベストアンサー

float.h のテスト結果がおかしい

 float.h  をテストするため、次のようなプログラムを作りました。  float.h に書かれている値が書き出されると思ったのですが、少し値が違います。  FLT_MAX と DBL_MAX の値が違っています。本当にこのヘッダファイルを読んでいるかと思い、 #define FLT_MAX 3.4e+3f    と変更すれば、 Max= 3.400000000000000000E+03 と出てきます。  一致しない原因は何でしょう。  コンパイラはルネサスのHEWで、CPUはH8/3052です。 *** float.h *** #define FLT_MAX 3.4028235677973364e+38f #define FLT_MIN 1.175494351e-38F #define DBL_MAX 1.7976931348623158e+308 #define DBL_MIN 2.2250738585072014e-308 *** プログラム *** sprintf(str," float :%ldBytes Max= %.18E Min= %.18E\r\n", sizeof(float), FLT_MAX, FLT_MIN) ; Put_str(str) ; sprintf(str," double:%ldBytes Max= %.18E Min= %.18E\r\n", sizeof(double), DBL_MAX, DBL_MIN) ; Put_str(str) ; *** 結果 *** float :4Bytes Max= 3.402823466385288600E+38         Min= 1.175494350822287500E-38 double:8Bytes Max= 1.797693134862315700E+308          Min= 2.225073858507201400E-308 *** まとめて整理 ***  元の原稿は比較し易いように縦に数字が並ぶように書いているのですが、 ここに書き込むとずれてしまいます。 #define FLT_MAX 3.4028235677973364e+38f         Max= 3.402823466385288600E+38  FLT_MIN に比べ有効桁数が多いのが気になる。  同じ有効桁数で区切っても ...567 と ...466 とでは誤差が大きい。    FLT_MAX 3.402823567 7973364e+38f       Max= 3.402823466 385288600E+38     FLT_MIN 1.175494351e-38F #define FLT_MIN 1.175494351e-38F         Min= 1.175494350822287500E-38  四捨五入なら分かる。 #define DBL_MAX 1.7976931348623158e+308         Max= 1.797693134862315700E+308  最後が8と7で違う。しかし、DBL_MAX は8バイトで表せる値に対して四捨五入したために7が8になったと考えれば納得出来る。 #define DBL_MIN 2.2250738585072014e-308         Min= 2.225073858507201400E-308  ピッタリ合っている  宜しくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.4

> 誤差が拡大するのは分かるのですが、次の部分の誤差が大き過ぎると思います。 FLT_DIGを調べれば分かることですが、単精度浮動小数点数の精度は10進法で6桁程度しかありません。

ricardo_
質問者

お礼

 回答有り難う御座います。  HEWのマニュアルに浮動小数点数の仕様が書いて有りました。  それから最大値を計算すると、次のようになりました。 16777215 * 2^104 = 3.402823466 3852885981170418348452e+38  すると、次の値が妥当のようです。 #define FLT_MAX 3.402823466e+38F  更に最大値の次の値は下のようになりました。 16777215 * 2^104 = 3.402823466 3852885981170418348452e+38 最大値 16777214 * 2^104 = 3.402823263 5611925616003375953727e+38 最大値の次の値  3.402823466 と 3.402823263 の差が大きいのが分かります。  これで、次の疑問も解けました。 ----------------------------  FLT_MIN に比べ有効桁数が多いのが気になる。  同じ有効桁数で区切っても ...567 と ...466 とでは誤差が大きい。    FLT_MAX 3.402823567 7973364e+38f       Max= 3.402823466 385288600E+38     FLT_MIN 1.175494351e-38F ----------------------------  まだ次のように、実験結果が今までの計算と合っているのに、HEWの ヘッダの値が他と違うと言う疑問が残りますが、大体の疑問が解けました。  みなさん、有り難う御座いました。 #define FLT_MAX 3.402823567 7973364e+38f HEW 実験結果  Max= 3.402823466 385288600E+38

その他の回答 (4)

回答No.5

>誤差については分かるのですが、次の部分の誤差が大き過ぎると思っています。 単精度の仮数部は、省かれた1ビットを含め、25ビットしかありません。 つまり「2の25乗」の精度しかありません。 「2の25乗」は「33554432」ですから「10進数で7桁」だけが有効です(2進数の0~33554432の範囲で表わせる10進数は0~9999999までの7桁) 「7桁だけ有効」と言うことは「整数部1桁+小数部6桁」しか信じられないので、小数部6桁で切ると #define FLT_MAX 3.402823 5677973364e+38f HEW 実験結果  Max= 3.402823 466385288600E+38 となり、切った小数7桁以下は「無効なので無視しなければならない桁」です。 小数7桁以下を無視すると、どちらも「同じ数値」になります。 言い換えると「小数点以下7桁目が4でも5でも、2進化して単精度の浮動小数点数にコンバートすると、同一のバイナリ値が出来あがる」って事です(小数点以下7桁目が3とか2とかだと、微妙に小さい値になってしまうでしょうから駄目です。また、6とか7とかだとオーバーフローの危険があるので駄目です) 試しに float f1; float f2; f1=3.4028235677973364e+38f; f2=3.402823466385288600E+38f; sprintf(str,"%s\r\n",f1 == f2 ? "EQU" : "Not EQU"); Put_str(str); と言うプログラムを書いてみましょう。 実行すると EQU になる筈です。 この結果により「10進表記が異なっていても、異なるのは有効桁の外(つまり無効桁)なので、内部のバイナリ値が一緒になり、f1とf2が等しくなる」のが判るでしょう。

ricardo_
質問者

お礼

 回答有り難う御座います。回答を書くのに時間が掛かったでしょう。  有効桁数が意外に少ないと言うのは、回答番号4のお礼に有るように分かりました。  みなさんが良く回答してくれるので更に調べ、次の事が分かりました。 (2^24 - 1) * 2^104 = 3.402823466 3852885981170418348452e+38 3.402823466E+38F  【比較用】あるメーカのFLT_MAX (2^25 - 1) * 2^103 = 3.4028235677973366 163753939545814e+38 3.4028235677973364e+38f 【比較用】ルネサスと秋月のFLT_MAX (2^24 - 1)は、111111... と1が23個並んだ2進数を10進数に変換した物。1111 を求めるのに (10000 - 1)として計算。 仮数22ビット+上位の固定1 で合計23ビットの1。  ルネサスの FLT_MAX に近い物が得られました。これで納得と行きたい所ですが、まだ最後の所が366と364で、2の違いが有りますね。  四捨五入でも納得出来ないし、何だろう。パソコン・アクセサリの電卓のバグだったりして。電卓にも今回と同じような問題が有り、表示されていても無効な数字なのかも知れません。  次のように変更して計算してみましたが、同じ結果でした。少しは変化すると思ったのですが、全く同じと言うのも意外です。 方法1 2^103 * (2^25 - 1) 方法2 2^128 - 2^103  ここまで分かったから、もういいかなと思っています。

回答No.3

定義は合ってますが、表示方法が間違っています。 sprintfなどの「printf系関数」は「単精度実数を引数に与えると、倍精度実数に変換する」のです。 この変換の際に、4バイトから8バイトに拡張する為、大きく誤差が出ます。 しかも、10進数表記(「0.123E+30」など、人間が理解出来る表記)にする際に、丸め誤差など、更に誤差が蓄積します。 簡単に言うと「貴方が目で見ている表示は、いろいろなキャストや変換が行われた後の結果で、元の数値とは似ても似つかないもの」なのです。 少なくとも「float⇒double」のキャストによって「全然違う値」になります。 実験するのであれば「FLT_MAX、FLT_MINを、floatの変数に代入し、変数の中身を16進ダンプする」のが正解です。 そして、16進ダンプした結果を見て、バイナリで「指数部が最大値になってて、仮数部が全部1で埋まってる」のを確認しましょう。 因みに「3.4028235677973364e+38fと書かれたソース上の文字列を、floatのバイナリ値に変換する際」に「既に誤差を含んでいる」のをお忘れ無く。 何故なら「10進数での有限小数が、2進数では無限小数になる」からです(「0.1」は「10進数では有限小数」ですが「2進数では循環小数」になり、0.1を2進数で正確に表現する事は不可能なのです) つまり「3.4028235677973364e+38fと言う10進数の数値は、2進数では正確に表現不可能」なのです。

ricardo_
質問者

お礼

 いつも回答有り難う御座います。  誤差については分かるのですが、次の部分の誤差が大き過ぎると思っています。  有効桁数が違うから、下半分の桁はゴミだと言うのは分かります。 -------------------------  FLT_MIN に比べ有効桁数が多いのが気になる。  同じ有効桁数で区切っても ...567 と ...466 とでは誤差が大きい。    FLT_MAX 3.402823567 7973364e+38f       Max= 3.402823466 385288600E+38     FLT_MIN 1.175494351e-38F

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.2

H8のHEWは長い間触っていないので詳しいことは忘れましたが、SHのものはfloatとdoubleを常に同じ(両方単精度または倍制度)にするオプションがあったように思います。 それと同じか、同じなくてもヘッダを使い回している可能性がありますので、有効桁数以上を記述していること自体はそれほど問題ありません。 ただ、floatが単精度で、doubleが倍精度の場合、FLT_MAXはどうしても単精度の有効桁数までしか表現できません。そして、可変個数引数としてprintfに渡す際にdoubleに変換されますので、その辺りで誤差が拡大することになります。

ricardo_
質問者

お礼

 いつも回答ありがとう御座います。 >floatとdoubleを常に同じ(両方単精度または倍制度)にするオプションがあったように思います。  調べると、倍精度を単精度にするオプションが有りました。  誤差が拡大するのは分かるのですが、次の部分の誤差が大き過ぎると思います。 -----------------------------  同じ有効桁数で区切っても ...567 と ...466 とでは誤差が大きい。    FLT_MAX 3.402823567 7973364e+38f       Max= 3.402823466 385288600E+38

  • php504
  • ベストアンサー率42% (926/2160)
回答No.1

実際のfloatで表現できる最大値以上を定義しているからでしょう そのfloat.hのFLT_MAXは明らかにおかしいですね 誰かが書き換えたんじゃないですか ちなみにVisualC++では #define FLT_MAX 3.402823466e+38F でした

ricardo_
質問者

お礼

 早速の回答有り難う御座います。 >そのfloat.hのFLT_MAXは明らかにおかしいですね 誰かが書き換えたんじゃないですか それは有りません。ルネサスからインストールして、私個人で使っていますから。  調べてみると、下記のように幾つか有りました。  (2)の方法は(1)よりも1桁少なくなっています。これは四捨五入のためじゃないかと思っています。  すなわち(1)の方式だと 1/3 * 3 としたときに 0.9999999 と表示され、(2)の方式だと 1.000000 と表示されるんじゃないかと思っています。(少数以下の桁数は適当に書きました。) 秋月電商のCコンパイラ #define FLT_MAX 3.4028235677973364e+38f #define FLT_MIN 7.0064923216240862e-46f ルネサスHEW #define FLT_MAX 3.4028235677973364e+38f #define FLT_MIN 1.175494351e-38F FLT_MAX 3.402823466E+38F (1) FLT_MIN 1.175494351E-38F FLT_MAX 3.40282347e+38f (2)有効桁数が1桁少ない FLT_MIN 1.17549435e-38f #define FLT_MAX 3.40282347e+38F #define FLT_MIN 1.17549435e-38F #define FLT_MAX 3.402823466e+38

関連するQ&A