• ベストアンサー

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)

  • chie65535
  • ベストアンサー率43% (8516/19358)
回答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  ここまで分かったから、もういいかなと思っています。

  • chie65535
  • ベストアンサー率43% (8516/19358)
回答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

  • 浮動小数点数の float型 での最少値について調べています。

    浮動小数点数の float型 での最少値について調べています。 「Visual C++ 2008 Express Edition」の「float.h」の中には、    #define FLT_MIN     1.175494351e-38F   /* min positive value */    #define FLT_MIN_EXP  (-125)           /* min binary exponent */ という記述があります。 float型で表現できる最少値が「1.175494351e-38F」なのだろうと思い、 下記のように 2^-125 を計算してみました。    printf("結果は、「%e」です。", pow((double)2, (double)(-125))); 実行結果は、    結果は、「2.350989e-038」です。 となり、「float.h」の「FLT_MIN」には一致しませんでした。 試しに、下記のようにしてみると( 2^-125 を 2^-126 に変えてみました )    printf("結果は、「%e」です。", pow((double)2, (double)(-126)));    結果は、「1.175494e-038」です。 となって「float.h」の「FLT_MIN」と「ほぼ一致」しているように見えます。 float型での最小値(FLT_MIN)を計算する際に「float.h」での「FLT_MIN_EXP」を使って    2^-125 とするのは間違っているのでしょうか?

  • PHPでε(イプシロン)のような定数はありますか?

    C言語では、 >1.0 + ε > 1.0 となる最小の値ε >#define FLT_EPSILON 1.19209290E-07F >#define DBL_EPSILON 2.2204460492503131E-16 >#define LDBL_EPSILON 1.084202172485504434e-019L のような形で定義されているそうですが、 PHPにおいてはそのような値は定義されているのでしょうか? そもそも小数点以下を評価(比べる)する際に C言語などと違う点はどこらへんでしょうか?

    • 締切済み
    • PHP
  • 文字列をfloatで読み込む(atoi,sscanf)。しかし、値がおかしい。

    お世話になっています。 C言語の質問です。 文字列をfloatで読み込もうとしているのですが、出力結果がおかしくて困っています。 文字列をatofで変換した場合、doubleでは上手く表示できるのですが、floatでは少数が上手く表示できません。 また、sscanfでも試したのですが、上手く表示できませんでした。 どうしても、doubleを使わずにfloatであらわしたいと考えています。 どうかこのプログラムの問題点のご指摘お願いします。 実行結果 53.196600 53.196602 53.196602 ソース #include<stdio.h> #include<stdlib.h> #include<string.h> main() { char str[100]="53.1966"; double b; float c,d; b = atof(str); printf("%f\n",b); c = (float)atof(str); printf("%f\n",c); sscanf(str,"%f",&d); printf("%f\n",d); } 開発環境 windowsXP cygwin

  • cプログラム

    次の10人の身長を入れ、最大と最小を配列を使って求めるプログラムなんですが、この場合だと一人の身長データしか入力できません どのように直せばいいでしょうか? #include <stdio.h> main () { float h[10],max,min; int i; max=-999; min=999; for(i=0;i<=9;i=i+1){ printf("%d番目の身長を入力してください\n",i+1); scanf("%5.1f",&h[i]); if(h[i]>max){ max=h[i]; } if(h[i]<min){ min=h[i]; } } printf("最大の身長は%5.1f,最小の身長は%5.1fです。\n",max,min); return(0); }

  • H8/3052 クロスコンパイラ環境下でのsprintfの使用について

    実務訓練でH8を用いて測定システムを開発しています。 今、H8に接続したセンサの値を測定しA/D変換した値を、フーリエ変換し、その結果をシリアル通信のソフト(ハイパーターミナル)などで表示できるようするために、その値を文字コードに変換して送るようにしているところです。 その変換した値がどのくらいなのかわからないので、場合わけ等が必要なのかと思っていましたら、会社の方にsprintfを使えばいいと言われました。 それは気づかなかったと思って、使ってみたら動きませんでした(^^; 型のあわせ方とかが間違っているのかな? char buf[100]; sprintf(buf,"%f",a[k]); //a[k]はフーリエ展開した係数の値(double) for(i=0;i<100;i++){ SCI_OUT_d(buf[i]); //SCI_OUT_dはシリアル送信関数 } なにかおかしいところありますか? ためしに char buf[100]; float DFTa; DFTa=(float)a[k]; sprintf(buf,"%f",a[k]); //a[k]はフーリエ転換した係数の値(double) for(i=0;i<100;i++){ SCI_OUT_d(buf[i]); //SCI_OUT_dはシリアル送信関数 } でfloat型にしてみてもそこでとまってしまいました。 何かH8のsprintfの実装についてわかることがある人がいましたら回答お願いします。

  • doubleで表現できる最も小さな値

    たとえば double num = 1.0; このnumよりも大きなdoubleで表現できる最も小さな値を生成したいと考えています。 FLT_MINを単純に足してもnum==num+FLT_MINとなってしまいます。 num != num2となるような最小値の作りかたを教えて下さい

  • ガウス関数を少しずつずらして足し上げるプログラム

    ある値をある検出器に入力したときに出力結果がその入力したを中心にガウス関数のように広がってしまう場合を考え,ある関数の値を小刻みに入力したとき、その関数が出力されたときそれぞれ値のガウス関数の広がりにより重ね合わされてどのように見えるか数値計算してみようと思い、以下のソースコードを書いて見ました。行列で考えてそれぞれの値の寄与を足しあげて見ようとしたのしたのですが、実行結果をみると明らかにおかしい10^19などの数値が見られます。これはなぜでしょうか。私のソースコードのどこに問題があるのでしょうか。お手数をお掛けしますが回答よろしくおねがいします。 インデントが反映されていませんでしたらすみません。 ---------------------------ソースコード------------------------------------------ #include<stdio.h> #include <stdlib.h> #include<math.h> #define N 100 #define f(x) pow(x,-1.7) //この関数が検出器に入力したときどのように見えるか知りたい// void mat_product(double ec[N],double r[N][N],double e[N]) { int i,j; for(i=0; i<N; i++){ for(j=0; j<N;j++){ ec[i]+=r[i][j]*e[j]; } } } int main(void) { int i,j; double ec[N],r[N][N],e[N]; double max,min, ei,E,sigma,min1,min2,m,pi; sigma=0.08; //keV// max=10.5; min=0.5; min1=0.5; min2=0.5; m=0.5; ei=(max-min)/N; pi=3.141592; for(i=0; i<N ;i++){ for(j=0; j<N ;j++){ r[j][i]=(1/(pow(2*pi,0.5)*sigma))*exp(-1*pow(m-min1,2)/(2*pow(sigma,2))); m+=ei; } e[i]=f(min2); m=0.5; min1+=ei; min2+=ei; } mat_product(ec,r,e); for(i=0; i<N; i++){ printf("%g\t%g\n",min,ec[i]); min+=ei; } return 0; } ---------------------------出力結果--------------------------------- 0.5 22.0512 0.6 23.8139 0.7 18.9 0.8 14.9642 0.9 12.1864 1 10.1512 1.1 8.60991 1.2 7.4112 1.3 6.45833 1.4 5.68688 1.5 5.05253 1.6 4.52387 1.7 4.07814 1.8 3.69846 1.9 3.37209 2 3.08926 2.1 2.84239 2.2 2.62547 2.3 2.43375 2.4 2.26337 2.5 2.1112 2.6 1.66592 2.7 1.85166 2.8 1.7404 2.9 1.63941 3 1.54742 3.1 1.46337 3.2 1.38635 3.3 1.31558 3.4 4.47979e+30 3.5 1.19017 3.6 1.13444 3.7 1.08275 3.8 1.0347 3.9 0.989949 4 0.9482 4.1 0.909181 4.2 0.872652 4.3 0.838401 4.4 0.806238 4.5 0.775991 4.6 0.747509 4.7 0.720652 4.8 0.695296 4.9 0.671329 5 0.648648 5.1 0.627161 5.2 0.606783 5.3 0.587437 5.4 0.569053 5.5 0.551566 5.6 0.534917 5.7 0.519053 5.8 0.503924 5.9 0.489483 6 0.475689 6.1 0.462502 6.2 0.449887 6.3 0.437809 6.4 0.426239 6.5 0.415146 6.6 0.404506 6.7 0.394292 6.8 0.384482 6.9 0.375054 7 0.365988 7.1 0.357265 7.2 0.348868 7.3 0.34078 7.4 0.332986 7.5 0.325471 7.6 0.318222 7.7 0.311226 7.8 0.304472 7.9 0.297947 8 0.291642 8.1 0.285546 8.2 0.27965 8.3 0.273945 8.4 -0.0404521 8.5 0.263075 8.6 0.257894 8.7 0.252874 8.8 0.248007 8.9 0.243288 9 0.238709 9.1 7.46629e+19 9.2 0.229953 9.3 0.225764 9.4 0.221696 9.5 0.217743 9.6 0.2139 9.7 0.210164 9.8 0.206531 9.9 0.202996 10 0.199557 10.1 0.196209 10.2 0.192868 10.3 0.185672 10.4 0.140717

  • floatの値について

    初心者的質問です。暇な方お答え願います。 分かりやすく教えてくださると嬉しいです。 例えば、簡単な main() { float a; printf("値を入力して下さい ==> "); scanf("%f",&a); printf("入力した値は%fです",a); } という感じで値を入力して出力するプログラムがあったとします。 このとき,例えば値を 166.5と入力すれば、166.500000とでますが 166.7と入力すると、166.699997とでるのはなぜでしょうか? doubleで変数宣言した場合は、このようにならないのですが。

  • 正規分布するプログラムを教えてください。

    正規分布する乱数プログラムを作りたいのですが、うまく作れません・・。 プログラムソースは長くなりますので見ていただかなくても結構なのですが、下記のようなプログラムを実行したところ、実行結果下記になり、正規分布にはなりませんでした・・。 色々ネットで調べたものの理解できないのでどなたか教えていただけないでしょうか>< 正規分布を利用して、例えば50~100位の間に分布する乱数を生成したりしたいのです。。。 #include <math.h> #include <time.h> #include <stdlib.h> #include <stdio.h> #define PI 3.14159265358979323846264 double p_nor(){ double rnd,t,u,r1,r2; rnd=rand()%10000/10000.0; t=sqrt(-2.0 * log(1-rnd)); u=2*PI*rnd; r1=t*sin(u); return r1; } int main(){ int i,bunpu[30]={}; double p,min=0,max=0,total=0; srand((unsigned)time(NULL)); for(i=0;i<100000;i++){ p=p_nor(); for(int j=0;j<30;j++){ if(p>-2.0+0.1*j && p<=-1.9+0.1*j) bunpu[j]++; } if(min>p)min=p; if(max<p)max=p; total+=p; } printf("min:%f max%f 平均%f\n",min,max,total/100000); for(int j=0;j<30;j++){ for(i=0;i<bunpu[j]/200;i++){ printf("*"); } printf("\n"); } return 0; } 実行結果 min:-1.711381 max0.803275 平均-0.247841 **************** ************************************ ******************** **************** ************** ************ *********** ********** ********** ********* ********* ******** ******** ******** ******** ******** ******** ******** ************************************* ************************* *********************** *********************** ************************ **************************** ******************************** **************************************************************** ***********

  • C言語イプシロン値

    現在使用中の開発環境(C言語使用)標準ヘッダfloat.hに単精度浮動小数点数イプシロン値が下記のように定義されています。 #define  FLT_EPSILON   0x8p-26F 「0x8p-26F」なる表現を初めて見たのですが、これはどういう意味で、具体的にどんな数字になるのでしょう? ご存じの方、教えてくださいませ。 よろしくお願いします。

専門家に質問してみよう