• ベストアンサー

型変換の問題?

次のプログラムなんですが #include <stdio.h> #include <math.h> #include <cstdlib> #include <iostream.h> void main(){ double v[10000]; for(int b=0;b<10000;b++){ v[b]=2.5+0.01*(double)(rand()%249+1);} for(b=0;b<10000;b++){ int c=(int)(100.*v[b])-251; if(c==192){printf("%d %f\n",c,v[b]);} }} 私の環境のVC++6.0 on win2kのもとで やってみると、printf("%d %f\n",c,v[b])のところで 192 4.43 となったり 192 4.42 となったりします。 真の値は、192 4.43のつもりでプログラムを組んだのですが、 なぜかc=(int)(100.*v[b])-251;という写像は 単射になっていないようです。 さて、質問ですが、double→int型の型変換に原因があるのでしょうか?

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

  • ベストアンサー
回答No.3

実数の計算をするからです。実数がコンピュータ内部でどのように表現されているかご存じですか?データは全て2進数で表されます。このとき、各桁の重みは整数の方は2^nですが、小数は(1/2)^nであらわされ、 0.5,0.25,0.125,0.0625,0.03125,0.015625,... となります。計算過程は、この数字で表現可能でしょうか?例えば、0.01という数字は、すでに表現不可能な数字です。表現不可能な数字は、表現可能な範囲で丸められるので、必ず誤差が生じます。%fを、%.16fとしてみてください。誤差が表示されます。 on Solaris 192 4.4399999999999995 192 4.4299999999999997 %fでの表示上は「4.42」ですが、実際には「4.4299999999999997(まだ続く)」という値で計算がされているということです。これは、 fprintf("%.16f / %.16f\n", 0.0625, 0.1); とかすれば、よくわかると思います。また、doubleからintは縮小変換になるので、何らかの桁落ちが発生します(64ビット→32ビット)。そのあたりでも誤差が発生していると思われます。 #縮小変換はVCではワーニングがでませんか? 精度が欲しいなら、欲しい精度が全て整数になるようにしてから計算するか、1桁ずつ計算するしかありません。探せば、COBOLのように10進数として計算(←用語忘れた)するライブラリがあるかもしれません。 ちなみに、 void main() { int v[10000]; int b; for (b = 0; b < 10000; b++) { v[b] = 250 + 1 * (rand() % 249 + 1); } for (b = 0; b < 10000; b++) { int c = (v[b]) - 251; if (c == 192) { printf("%d %d\n", c, v[b]); } } } 数字がそろいました。

noname#108554
質問者

お礼

そうやって誤差を表示すれば一目瞭然です。 ありがとうございました。 >#縮小変換はVCではワーニングがでませんか? いや、でないです。 しかし、わずか小数点2桁で誤差が 発生するとは驚きです。

その他の回答 (3)

  • ranx
  • ベストアンサー率24% (357/1463)
回答No.4

doubleからintへの変換ですから、どうしたって単射にはなりえないですよね。 (5.2と5.3はどちらも5になる。) printfで%fの表示桁数がこんなに小さいのはどこのライブラリなのか 疑問ですが、それはさておき、原因はキャストとprintfの仕様の違いに あると思います。 キャストは端数を切り捨てて整数化しますが、printfは末尾を四捨五入して 表示します。ですから、例えばv[b]が4.439だったらcは192になりますが、 printf("%.2f",v[b])で表示すれば4.44になります。 4.42は何でしょうねえ。

noname#108554
質問者

補足

>doubleからintへの変換ですから、どうしたって単射にはなりえないですよね。 しまった・・・言い訳です。 すみません、単射の意味をプログラムに合わせて勝手に 頭の中で修正してしていました。 この場合の値域は、実数全体ではなくて2.51~4.99までで 0.01区切りなので、自然数みたいなものです。 いや、やりたいのは、配列のindexに実数はとれないので int型にcastしたいんだけど、精度がね・・・っていうことです。

回答No.2

#1 : > 余談ですが、for文のパラメータの中で変数の宣言を > するのはVC++では認められているのかもしれませんが > 本来は間違いです。 異議アリ。このコードがC++ならば(iostream.hをincludeしているので間違いなくC++でしょう)きわめて合法です。

noname#108554
質問者

お礼

最近、正統的なプログラムをしたいと考えていましたので #1さんの指摘にはどきどきしましたが、問題ないですよね。

  • shige_70
  • ベストアンサー率17% (168/946)
回答No.1

Solaris,gccの環境で試しましたが、4.43と4.44が出ました。 浮動小数点はどうしても誤差がでます。 小数部の切り捨てで誤差が境界を越えてしまったのでしょう。 この場合でしたら、intにキャストする前に0.5を足して四捨五入扱いにするなどで回避するしかないでしょう。 余談ですが、for文のパラメータの中で変数の宣言をするのはVC++では認められているのかもしれませんが本来は間違いです。

noname#108554
質問者

お礼

ありがとうございます。 やっぱり精度の問題みたいです。

関連するQ&A

専門家に質問してみよう