- ベストアンサー
ceilでの切り上げ処理について
WinXP,SDKです。 小数点以下第2位までに切り上げをしようと思ったのですが、 以下のような式の場合に0.55が0.56と求められてしまいます。 double num = 0.55; num = num * 100; <- この時点で55.0 double tmp = ceil(num); <- この時点で56.0 ※ num = tmp / 100; <- この時点で0.56 ※の所でなぜ55.0と戻ってこないのでしょうか。 これはなぜか0.55の時だけ現象が出てしまいます。 ほかの数値の場合はほぼ上手く行きます。(ダメな数値は見つけられていません) 上の式をたとえば num = 55.0; double tmp = ceil(num); とした場合、正しく55.0と求められます。 おそらく100をかけているところで何らかの問題が出ていると思うのですが… 100をintやdouble型にしてみても結果は同じでした。 どのように計算すれば上手くいくのでしょうか。 アドバイスをお願い致します。
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
2進の小数点以下は近似値でしかないのです。 2の-1乗は0.5、-2乗は0.25・・・ なので0.55をセットした段階で近似値に置き換わっていると思われます。 ただ、デバッグなのど表示では暗黙の丸目が行われていて0.55と表示されているだけでしょう 有効桁で一度丸めを行い切り上げを行うしかないのかも。 私の場合は文字列に変換してから行いましたけど
その他の回答 (5)
- redfox63
- ベストアンサー率71% (1325/1856)
0.55自体が2進表現にした場合に厳密な0.55にならないに起きる現象でしょう double num = 0.55; ddum *= 100.0; double t1 = floor( num ); double t2 = num - t1; if ( t2 == 0 ) { printf( "num == 55\t" ); } else { printf( "num != 55\t" ); } printf( "%lf", t2 ); printf( "%.20lf", 0.55 ); num = 55.0; double tmp = ceil(num); で tmpが55になるのは 55を浮動小数点であらわしても55のまま誤差が無いためです
お礼
分かりやすいアドバイスありがとうございます。 何だか基本的な事を分かっていなかったように思います。 55.0を直接入れた時は大丈夫というのもやっと理解出来ました。 こうなるのは当然とやっと分かったので、 処置を考える事が出来そうです。 ありがとうございました。
- titokani
- ベストアンサー率19% (341/1726)
浮動小数点数で0.55は循環小数になってしまうので、正確に計算することはできません。 多分他にもだめな値はあると思います。 こういった場合には、最初からすべての値を100倍して計算し、最終的な表示の際に100分の1に戻す方法がよく使われます。
お礼
アドバイスありがとうございます。 循環小数…このような結果になるのは当たり前なのですね。 分かっているようで分かっていなかったように思います。 0.55*100だけが特殊な訳ではないのですね。 プログラムとしては、小数点以下の有効桁数は6桁で、 0.55*100の小数点以下で0以外のものが発生するのが 15桁目くらいなので、その間くらいで切り捨てをしてから 処理しようかと思います。 ありがとうございました。
- Tacosan
- ベストアンサー率23% (3656/15482)
printf("%.20f\n", num); を入れて実行すればわかると思う.
お礼
ありがとうございます。 下の方へお礼として書いていましたが、 かなり深いところに「7」が入っていました。 最初からやってみたらすぐにピンと来たはずなのに、 随分長いこと悩んでいました。 ありがとうございます。
- php504
- ベストアンサー率42% (926/2160)
やりたいのは切り上げですね 間違えました
お礼
見て頂いてありがとうございました。 切り上げとか数値の問題ではないのかもしれません… あれから色々と調べていて、 普通に double tmp = 0.55 * 100; などとしたものを%.20fで表示してみると、 小数点以下第15位くらいに値が入っています。 きっとこれのせいでceilが一つ上に切り上げしてくるんですね。 これは誤差と考えて、 計算式を一度100億倍位にして、切り捨て(floor)してから ceilにかけたりしてみようかと思います。 ありがとうございました。
- php504
- ベストアンサー率42% (926/2160)
計算結果をintにしたら num = (int)(num * 100);
お礼
アドバイスありがとうございます。 はい、デバッグでは小数点以下第14位までしか表示されておらずに、 そこまではすべてゼロだった為になかなか気づきませんでした。 実際は"55.00000000000000700000"と、15位に"7"が入っていました。 今回丸めを行おうと思います。 でも、文字列でやったほうが正しく出来るケースも昔体験したような気がするのですが… 迷っています。 ありがとうございました。