を使用==
して浮動小数点変数の等価性をチェックすることは良い方法ではありません。しかし、私は次のステートメントでそれを知りたいだけです:
float x = ...
float y = x;
assert(y == x)
y
はからコピーされるのでx
、アサーションはtrueになりますか?
-m32
)か、GCCにx87 FPUを使用するように指示する()ことで再現できます-mfpmath=387
。
を使用==
して浮動小数点変数の等価性をチェックすることは良い方法ではありません。しかし、私は次のステートメントでそれを知りたいだけです:
float x = ...
float y = x;
assert(y == x)
y
はからコピーされるのでx
、アサーションはtrueになりますか?
-m32
)か、GCCにx87 FPUを使用するように指示する()ことで再現できます-mfpmath=387
。
回答:
assert(NaN==NaN);
kmdrekoによって指摘されたケースに加えて、x87-mathで80ビット浮動小数点が一時的にメモリに格納され、後でレジスター内にまだ格納されている値と比較される場合があります。
可能な最小の例-O2 -m32
:でコンパイルするとgcc9.2で失敗します:
#include <cassert>
int main(int argc, char**){
float x = 1.f/(argc+2);
volatile float y = x;
assert(x==y);
}
Godboltデモ:https ://godbolt.org/z/X-Xt4R
volatile
あなたは十分なレジスタ圧力を作成するために管理している場合、おそらくしているために、省略することができますy
保存され、メモリから再ロード(ただし、コンパイラは十分、すべて一緒に比較を省略しないように混乱します)。
GCC FAQリファレンスを参照してください:
float
標準の精度と余分な精度を比較するときに、余分なビットが考慮されるのは奇妙に思われます。
-ffloat-store
これはこれを防ぐ方法のように思われることに言及する価値があります。
onの比較は常にfalse(yesでさえ)であるため、x
isの場合はtrueになりません。他のすべてのケース(通常値、非正規値、無限大、ゼロ)の場合、このアサーションは真になります。NaN
NaN
NaN == NaN
浮動小数点を回避する==
ためのアドバイスは、浮動小数点数が算術式で使用されると多くの結果を正確に表現できないため、計算に適用されます。割り当ては計算ではなく、割り当てが元の値と異なる値になる理由はありません。
標準に準拠している場合、拡張精度評価は問題にはなりません。<cfloat>
C からの継承[5.2.4.2.2.8](強調鉱山):
割り当てとキャスト(余分な範囲と精度をすべて削除する)を除いて、通常の算術変換の対象となる浮動オペランドと値を持つ演算の値と浮動定数の値は、範囲と精度がタイプ。
ただし、コメントで指摘されているように、特定のコンパイラ、ビルドオプション、およびターゲットを使用した場合、これが逆説的に誤りになることがあります。
x
最初の行のレジスタで計算され、aの最小値よりも高い精度を維持するとどうなりますかfloat
。y = x
唯一の維持、メモリであってもよいfloat
精度。次に、同等性のテストは、レジスタに対してメモリを使用して、異なる精度で行われるため、保証はありません。
x+pow(b,2)==x+pow(a,3)
auto one=x+pow(b,2); auto two=y+pow(a,3); one==two
一方が他方よりも高い精度を使用して比較する可能性があるため、異なる可能性があります(1/2が64ビット値のRAMであり、中間値がfpuで80ビットの場合)。したがって、割り当ては時々何かを行うことができます。
gcc -ffloat-store
厳密なコンプライアンスのためにそれを強制することができます。しかし、この質問はx=y; x==y;
、どちらの変数に対しても何もしないことに関するものです。 がfloatに収まるように丸められている場合y
、doubleまたはlong doubleに変換してから戻すと、値は変更されません。...
はい、y
確実に次の価値を引き受けますx
:
[expr.ass]/2
:単純な代入(=)では、左のオペランドによって参照されるオブジェクトは、その値を右のオペランドの結果に置き換えることによって変更されます([defns.access])。
他の値を割り当てる余裕はありません。
(他の人たちは、等価比較==
がそれでもfalse
NaN値に対して評価されることをすでに指摘しています。)
浮動小数点に関する通常の問題==
は、あなたが思っているほどの価値を持たないことは簡単なことです。ここでは、2つの値が同じであることを知っています。
はい、すべての場合(NaNとx87の問題を無視)、これは当てはまります。
そうした場合memcmp
、それらの上にNaNとsNaNsを比較することでありながら、あなたは平等のためにできるテストになります。これには、値をfloat
80 ビットではなく32ビットに強制する変数のアドレスを取得するコンパイラも必要になります。これにより、x87の問題が解消されます。ここでの2番目のアサーション==
は、NaNをtrueと比較しないことを示さないようにすることを目的としています。
#include <cmath>
#include <cassert>
#include <cstring>
int main(void)
{
float x = std::nan("");
float y = x;
assert(!std::memcmp(&y, &x, sizeof(float)));
assert(y == x);
return 0;
}
NaNの内部表現が異なる(仮数が異なる)場合、memcmp
trueは比較されないことに注意してください。
通常の場合は、trueと評価されます。(またはassertステートメントは何もしません)
編集:
「通常のケース」とは、他のユーザーから指摘された前述のシナリオ(NaN値や80x87浮動小数点単位など)を除外することを意味します。
今日の状況で8087チップが陳腐化していることを考えると、問題はかなり孤立しており、使用される浮動小数点アーキテクチャの現在の状態に問題が当てはまるため、NaNを除くすべてのケースに当てはまります。
(8087に関するリファレンス-https ://home.deec.uc.pt/~jlobo/tc/artofasm/ch14/ch143.htm)
良い例を再現した@chtzとNaNについて言及した@kmdrekoへの称賛-以前はそれらについて知らなかった!
x
されている間y
は、浮動小数点レジスタに入ることが完全に可能であると思いました。メモリはレジスタよりも精度が低く、比較が失敗する可能性があります。
float
余分な精度のない値である必要があります。
int a=1; int b=a; assert( a==b );
アサーションをスローする可能性があることを考えると、正しく機能しているコンパイラに関してこの質問に答えるのは理にかなっていると思います(ただし、一部のコンパイラのバージョンによっては、 -これを誤解することがわかっている)。実際には、何らかの理由でコンパイラがレジスタに格納された割り当ての結果から余分な精度を削除しない場合、その値を使用する前に削除する必要があります。
はい、NaNの場合を除いて、常にTrueを返します。変数値がNaNの場合、常にFalseを返します。