2017-12-21 64 views
4

浮動小数点に関連する問題を完全に理解していますが、私は説明できない非常に興味深い動作を見てきました。浮動小数点数の不正確さを避ける

float x = 1028.25478; 
long int y = 102825478; 
float z = y/(float)100000.0; 
printf("x = %f ", x); 
printf("z = %f",z); 

出力は次のとおり

X = 1028.254761 Z =今1028.254780

フローティング番号私は変数xにその割り当てられた場合に、その特定の乱数値(1028.25478)を表すために失敗した場合。変数zの場合、なぜ同じではないのですか?

P.S.私はpellesC IDEを使ってコードをテストしています(C11コンパイラ)。

+0

あなたのコード[私のために同じものを印刷します](https://ideone.com/9zyYyF)。 – dasblinkenlight

+0

小数点以下を小数点以下を使用して印刷してみてください。 '"%.10f "'。 –

+0

これは複製ではありません。私はいくつかの実際の重複があると思いますが、見つけるのは難しいです。基本的に、コンパイラは2番目のケースでは「double」として数式を実行します。 – dasblinkenlight

答えて

5

ここで起こることは、後者の浮動小数点変数が省略され、代わりに倍精度レジスタに保持されることです。そしてそれはそのままでprintfの引数として渡されます。コンパイラは、デフォルトの引数のプロモーションの後で倍精度でこの数値を渡すことが安全だと考えます。

Iは、これらのスイッチとGCC 7.2.0、使用類似結果生成する管理:

-Wall -Werror -ffast-math -m32 -funsafe-math-optimizations -fexcess-precision=fast -O3 

出力

x = 1028.254761 z = 1028.254800 

数が若干異なる^です。

description for -fexcess-precision=fastは言う:

-fexcess-precision=style

このオプションは、浮動小数点演算は、IEEE規格より より精度又は範囲と形式で発生 マシンに過剰精度上さらなる制御を可能にし相互交換 浮動小数点型。既定では-fexcess-precision=fastは です。つまり、 がソースコードで指定された型よりも大きい場合は、 の精度で処理が実行され、ソースコードで指定された型が に丸められると予測できません。 Cをコンパイルする場合、-fexcess-precision=standardが指定されている場合、 超過精度はISO C99で指定された規則に従います。 特に、キャストと代入の両方で値が の意味型に丸められます(-ffloat-storeは の割り当てにのみ影響します)。このオプション[-fexcess-precision=standard]は、のような厳密な適合オプション が使用されている場合、Cのデフォルトで有効になります。 -ffast-math は、 の厳密な適合オプションが使用されているかどうかにかかわらず、デフォルトで-fexcess-precision=fastを有効にします。

この動作はC11準拠

+0

それは私にとっては大変なことですが、それはC11だと確信しています。 –

+0

また、CCFlagsは次のとおりです。-std:C11-Tx86-coff -Ot -Ob1 -fp:precise -W1 -Gd –

+0

ええ、コンパイラが正しくない場合があります。 Pelle'sからアセンブラの出力*を得ることができますか? –

2

IEEE754厳格な浮動小数点にこれを制限しないで、答え同じである必要があります。

1028.25478は、実際には1028.2547607421875です。それはxを占めます。

y/(float)100000.0;の評価では、yは、Cの引数昇格の規則によってfloatに変換されます。最も近いfloat102825478102825480です。 IEEE754は、除算の最良の結果を返すことを要求します。1028.2547607421875(値はz):1028.25480に最も近い数値です。

私の答えはあなたの観察された振る舞いとは逆です。私はあなたのコンパイラに浮動小数点を厳密に実装しないようにしています。おそらくIEEE754を実装していないかもしれません。

+1

そしてもちろん、CはIEEE-754に厳密に、あるいはまったく準拠する実装を必要としません。実際には、実際のところ、現代の汎用プラットフォームの実質的にすべての実装は、IEEE-754フォーマットと少なくともIEEE-754セマンティクスを使用しています。多くの場合、少なくともデフォルトでIEEE-754に厳密に準拠していません。 –

2

zdoubleであり、y/(float)100000.0y/100000.0であるかのようにコードが動作します。

float x = 1028.25478; 
long int y = 102825478; 
double z = y/100000.0; 

// output 
x = 1028.254761 z = 1028.254780 

重要な考慮事項は、FLT_EVAL_METHODです。これにより、選択された浮動小数点コードをより高い精度で評価することができます。 割り当てキャストを除い

#include <float.h> 
#include <stdio.h> 
printf("FLT_EVAL_METHOD %d\n", FLT_EVAL_METHOD); 

...、通常の算術変換を受けるフローティングオペランドと値を持つと浮動小数点定数の演算子によって得られた値は、その範囲と精度よいフォーマットに評価されますタイプによって必要以上に大きくなる。評価フォーマットの使用は、実装定義の値FLT_EVAL_METHODによって特徴付けられます。

-1不確定;
0は、 タイプの範囲と精度だけのすべての演算と定数を評価します。
1は、... 範囲とdoubleタイプの精度種類floatdouble評価long double タイプの範囲と精度... long double を評価します。
2 long doubleタイプの範囲と精度ですべてを評価してください。

しかし、これはfloat z = y/(float)100000.0;zが割り当てにすべてのより高い精度を失う必要があるようは適用されませんが。


Iコードが浮動小数点演算の予想ルールに対してより接着性を有している速度の最適化を使用していること@Antti Haapalaに同意します。

関連する問題