みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

IEEE754と浮動小数点定数同士の演算について

■質問
浮動小数点の標準規格IEEE754に、浮動小数点定数同士の演算に関する規定はありますか?

■背景
とあるマイコンで組込みソフト開発をしています。
このマイコン用のコンパイラ(IEEEに準拠)で以下のCソースコードをコンパイルしたところ、コンパイラのバージョン1とバージョン2で演算結果が異なりました。

double d_val = 6.6f * 10.0f;

○コンパイルバージョン1でコンパイルしたソフトの演算結果
→d_valには66.0が代入されました。
○コンパイルバージョン2でコンパイルしたソフトの演算結果
→d_valには65.999999…(詳細は失念)が代入されました。

コンパイラメーカーに問い合わせたところ、「バージョン1から2へのアップグレードにおいて、浮動小数点定数演算に関連する変更を行ったが、その影響は浮動小数点演算における誤差の範囲内である」との回答でした。

誤差の範囲内であるとはいえ、コンパイラのバージョン違いで演算結果が異なるのは困るので、これがIEEE754規格違反なのであれば、それを根拠にコンパイラメーカーへ対応を求められるのではないかと考えています。
しかし、浮動小数点定数同士の演算結果の正確性についての規定がないのであれば、開発側で対応するしかありません。

いろいろと調べてはいるのですが、これといった情報に行き当たっていないので、こちらで質問させていただきます。どうぞよろしくお願いいたします。

投稿日時 - 2012-11-22 07:30:49

QNo.7809838

すぐに回答ほしいです

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

>アセンブルコードを見ると、6.6f×10.0fという単精度定数同士の演算が、
>コンパイラのバージョン1では単精度同士で演算されてからdouble型に代入されており、
>バージョン2では、倍精度で演算されてからdouble型に代入されているようです。
正確なところは記憶していませんが、
2項演算では、まず両方の数の精度を精度の高いほうにそろえ、それから演算する。ただし、より高い精度で計算することも推奨する。
だったと思います。よって、どちらの計算方法も間違いでないと思います。
統計的に誤差が少ないのは倍精度に変換してから計算する方法ですが、計算時間が長くなります。

投稿日時 - 2012-11-22 13:34:56

お礼

お礼が遅くなりました。ご回答ありがとうございました。

大変参考になりました。
結局、私が知りたかったことは、IEEEの規約ではなくて、演算における(暗黙の)型変換に関するものであったようです。

投稿日時 - 2012-11-24 12:12:13

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(7)

ANo.7

もうひとつ、気になることがあるのですが、

>6.6f×10.0fという単精度定数同士の演算が・・・・で演算されてからdouble型に代入されており、
何のために、単精度の実数を、倍精度の実数に代入しているのでしょうか?
単精度の実数には、それなりの誤差が含まれていて、それは倍精度に代入しても残ります。(元の数が定数でも変数でも同じです。)
最終的に倍精度の結果がほしいのであれば、最初から倍精度の実数で計算しなければなりません。

投稿日時 - 2012-11-22 19:26:28

お礼

お礼が遅くなりました。ご回答ありがとうございました。

単精度同士の演算結果を倍精度型の変数に入れているのは、コンパイラの動作を確認するためのサンプルとして試したためです。

投稿日時 - 2012-11-24 12:07:42

ANo.6

これは、コンパイラや浮動小数点の処理を、古いコンパイラにそろえればよいという話ではなくて、そのプログラムが、浮動小数点の誤差について、何の対策もされていないという問題ではありませんか。
新しいコンパイラのほうが、あきらかに誤差の少ない処理になっているのに、動作がおかしくなるのであれば、間違いなくそういった現象が起きています。
そのようなプログラムが、まがりなりにもまともに動いたのは奇跡のような話で、今後もまともに動く保証は何もありません。
すぐにNO.2に書いたサイトを熟読して、「正しい」プログラムに修正するべきです。

投稿日時 - 2012-11-22 18:57:02

お礼

お礼が遅くなりました。ご回答ありがとうございました。

投稿日時 - 2012-11-24 12:12:43

ANo.5

> こうした処理の違いが、処理系依存と言ってよいのか

そのコンパイラ処理系すなわちソフトウェアに依存していると言えるでしょう。
そのような内部計算順序に関する規定はIEEE754には書かれていないでしょうし,ハードウェア依存の問題ではないでしょうから。

アセンブルコードまで調べていらっしゃるということは,
コンパイラver.2上でver.1の動作をさせたければ,次のように書けばよいし,
float f_val = 6.6f * 10.0f;
double d_val = f_val;
コンパイラver.1上でver.2の動作をさせたければ,次のように書けばよい,
double d_val = (double)6.6f * (double)10.0f;
左辺と右辺の型を合わせるよう配慮したコーディングをすることで回避できる問題ではないのですか。

投稿日時 - 2012-11-22 14:24:26

お礼

お礼が遅くなりました。ご回答ありがとうございました。

投稿日時 - 2012-11-24 11:53:13

ANo.3

蛇足の追加ですが、
No.1回答の2番目の丸めは、1番目の丸めより統計的に誤差が大きくなります。
質問の例では、旧コンパイラではプラス側に誤差が出ていることから2番目の丸め、新コンパイラでは1番目の丸めが使われていることが想像できます。
コンパイラが新しくなって、より誤差の小さい方法に変えられたのであれば、これを元に戻すように要求するのは筋違いの要求といえます。

投稿日時 - 2012-11-22 11:11:01

補足

ありがとうございます。

アセンブルコードを見ると、6.6f×10.0fという単精度定数同士の演算が、
コンパイラのバージョン1では単精度同士で演算されてからdouble型に代入されており、
バージョン2では、倍精度で演算されてからdouble型に代入されているようです。

それで、こうした処理の違いが、処理系依存と言ってよいのかが、知りたいところです。よろしくお願いいたします。

投稿日時 - 2012-11-22 12:40:56

お礼

お礼が遅くなりました。ご回答ありがとうございました。

投稿日時 - 2012-11-24 11:49:05

ANo.2

IEEEの規定についてはすでに回答がありますが(最近丸めが2種類あるので、そのどちらを採用するかで値が違うのは間違いでない)、

それとは別の話で、通常、浮動小数点の計算には誤差がつきものなので、ソフト側で対応するのは、常識です。

質問の例では、たまたま、旧バージョンのコンパイラでは問題となる誤差が発生しなかった(プラス側の誤差が発生している)のですが、別の値を使った計算では、どうなるかわかりません。

このソフトで、d_valがどのような使われ方をするのかわかりませんが、もしも
if ( d_val >= 66.0 ) { ・・・
というようなことをしているなら、浮動小数点の常識を知らない人のすることと言えます。

次のサイトに浮動小数点の誤差の考え方が載っていますから、適切な対応を考えましょう。
http://www.geocities.co.jp/SiliconValley-Bay/5308/

ついでに、どうでもよい蛇足ですが、No.1回答の補足として
最近丸めが2種類あるのは、いわゆる四捨五入が、一般に行われている方法とJISに規定されている方法
http://www.bsddiary.net/doc/jis-z8401.html
との2種類あるのと同じ理由です(2進数で同じことを考えている)。
それ以外の、切り上げ、切り捨て、は、おもに、誤差がプラス側に最大、マイナス側に最大で出たとき(まれな偶然でありうる)どの程度の誤差になるのかを知りたいとき使うものです。

投稿日時 - 2012-11-22 10:51:33

お礼

お礼が遅くなりました。ご回答ありがとうございました。

投稿日時 - 2012-11-24 11:48:19

ANo.1

--------
IEEE 754-2008標準では5種類の丸めアルゴリズムが定義されている。
最近接丸め(偶数)(round to nearest even)
最近接丸め(0から遠いほうへ)
0方向への丸め。切り捨て (rounding off, truncation)
+∞への丸め。切り上げ (rounding up, ceiling)
-∞への丸め。切り下げ (rounding down, floor)
http://ja.wikipedia.org/wiki/IEEE_754#.E6.B5.AE.E5.8B.95.E5.B0.8F.E6.95.B0.E7.82.B9.E6.95.B0.E3.81.AE.E4.B8.B8.E3.82.81
http://everything2.com/title/IEEE+754
--------
近似値への丸め
これがアプリケーション起動時のデフォルトモードです。通常の浮動小数点ライブラリでサポートされているモードは、このモードだけです。ハドウェア浮動小数点環境と、拡張浮動小数点ライブラリでは、4 つすべての丸めモードがサポートされています。
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0475ej/BABBCDDE.html
--------

ということで。

double d_val = 6.6f * 10.0f; で発生する誤差がもしも上記の丸め方式の違いに起因するものであるなら,IEEE754規格違反とは言えないでしょう。
上記に登場したARMコンパイラツールのように,通常ライブラリでは1種類の丸めモードしかサポートしないが拡張ライブラリではすべての丸めモードをサポートするというコンパイラ製品であるのなら,ライブラリを変えるだけで開発側での対応も楽になるでしょう。

投稿日時 - 2012-11-22 08:58:49

お礼

お礼が遅くなりました。ご回答ありがとうございました。

投稿日時 - 2012-11-24 11:47:46

あなたにオススメの質問