Saya tahu bahwa menggunakan ==
untuk memeriksa persamaan variabel floating-point bukanlah cara yang baik. Tapi saya hanya ingin tahu itu dengan pernyataan berikut:
float x = ...
float y = x;
assert(y == x)
Sejak y
disalin dari x
, akankah pernyataan itu benar?
c++
floating-point
Wei Li
sumber
sumber
-m32
), atau dengan menginstruksikan GCC untuk menggunakan x87 FPU (-mfpmath=387
).Jawaban:
Selain
assert(NaN==NaN);
case yang ditunjukkan oleh kmdreko, Anda dapat memiliki situasi dengan x87-matematika, ketika float 80bit disimpan sementara untuk memori dan kemudian dibandingkan dengan nilai-nilai yang masih disimpan di dalam register.Mungkin contoh minimal, yang gagal dengan gcc9.2 saat dikompilasi dengan
-O2 -m32
:Demo Godbolt: https://godbolt.org/z/X-Xt4R
Itu
volatile
mungkin dapat dihilangkan, jika Anda berhasil membuat daftar-tekanan yang cukup telahy
disimpan dan reloaded dari memori (tapi membingungkan cukup compiler, tidak menghilangkan perbandingan semua-bersama-sama).Lihat referensi FAQ GCC:
sumber
float
dengan presisi standar dengan presisi ekstra.-ffloat-store
tampaknya menjadi cara untuk mencegah hal ini.Ini tidak akan benar jika
x
iniNaN
, karena perbandingan padaNaN
yang selalu salah (ya, bahkanNaN == NaN
). Untuk semua kasus lain (nilai normal, nilai subnormal, infinities, nol) pernyataan ini akan benar.Saran untuk menghindari
==
mengapung berlaku untuk perhitungan karena angka floating point tidak dapat mengekspresikan banyak hasil persis ketika digunakan dalam ekspresi aritmatika. Tugas bukanlah perhitungan dan tidak ada alasan bahwa tugas akan menghasilkan nilai yang berbeda dari yang asli.Evaluasi presisi yang diperluas harus menjadi masalah jika standar diikuti. Dari
<cfloat>
diwarisi dari C [5.2.4.2.2.8] ( penekanan milik saya ):Namun, seperti yang ditunjukkan oleh komentar, beberapa kasus dengan penyusun, opsi bangun, dan target tertentu dapat menjadikan ini salah secara paradoks.
sumber
x
dihitung dalam register di baris pertama, menjaga presisi lebih dari minimum untuk afloat
. They = x
mungkin dalam memori, hanya menjagafloat
presisi. Maka tes untuk kesetaraan akan dilakukan dengan ingatan terhadap register, dengan tindakan berbeda, dan dengan demikian tidak ada jaminan.x+pow(b,2)==x+pow(a,3)
dapat berbeda dariauto one=x+pow(b,2); auto two=y+pow(a,3); one==two
karena satu dapat membandingkan menggunakan lebih presisi daripada yang lain (jika satu / dua adalah nilai 64 bit di ram, sedangkan nilai intermediste adalah bit 80ish pada fpu). Jadi tugas kadang bisa melakukan sesuatu.gcc -ffloat-store
kepatuhan yang ketat. Tetapi pertanyaan ini adalah tentangx=y; x==y;
tanpa melakukan apa pun pada keduanya. Jikay
sudah dibulatkan agar sesuai dengan float, mengkonversi ke double atau long double dan kembali tidak akan mengubah nilai. ...Ya,
y
pasti akan mengambil nilaix
:Tidak ada peluang untuk nilai-nilai lain yang ditugaskan.
(Yang lain telah menunjukkan bahwa perbandingan kesetaraan
==
akan tetap dievaluasifalse
untuk nilai NaN.)Masalah umum dengan floating-point
==
adalah mudah untuk tidak memiliki cukup nilai yang Anda pikir Anda lakukan. Di sini, kita tahu bahwa kedua nilai itu, apa pun itu, adalah sama.sumber
[expr]
. Jika saya mengabaikan tautan dan fokus pada kutipan, saya bingung dengan hal itu, misalnya C.5.3 tampaknya tidak membahas penggunaan istilah "nilai" atau istilah "hasil" (meskipun itu tidak gunakan "hasil" sekali dalam konteks bahasa Inggris yang normal). Mungkin Anda bisa lebih jelas menggambarkan di mana menurut Anda standar membuat perbedaan, dan memberikan satu kutipan yang jelas untuk hal ini terjadi. Terima kasih!Ya, dalam semua kasus (mengabaikan masalah NaNs dan x87), ini akan benar.
Jika Anda melakukan
memcmp
pada mereka, Anda akan dapat menguji kesetaraan sambil dapat membandingkan NaNs dan sNaNs. Ini juga akan meminta kompiler mengambil alamat variabel yang akan memaksa nilai menjadi 32-bitfloat
alih - alih yang 80-bit. Ini akan menghilangkan masalah x87. Penegasan kedua di sini dimaksudkan untuk gagal menunjukkan bahwa==
tidak akan membandingkan NaNs sebagai benar:Perhatikan bahwa jika NaNs memiliki representasi internal yang berbeda (mis. Mantissa yang berbeda), maka
memcmp
tidak akan membandingkan true.sumber
Dalam kasus biasa, itu akan dievaluasi ke true. (atau pernyataan tegas tidak akan melakukan apa-apa)
Edit :
Dengan 'kasus biasa' yang saya maksud adalah mengecualikan skenario yang disebutkan di atas (seperti nilai NaN dan unit floating point 80x87) sebagaimana ditunjukkan oleh pengguna lain.
Mengingat usang 8087 chip dalam konteks saat ini, masalah ini agak terisolasi dan untuk pertanyaan yang berlaku dalam keadaan saat ini arsitektur floating-point yang digunakan, itu berlaku untuk semua kasus kecuali untuk NaNs.
(referensi tentang 8087 - https://home.deec.uc.pt/~jlobo/tc/artofasm/ch14/ch143.htm )
Kudos to @chtz untuk mereproduksi contoh yang baik dan @kmdreko untuk menyebutkan NaNs - tidak tahu tentang mereka sebelumnya!
sumber
x
berada di register floating point sementaray
diambil dari memori. Memori mungkin memiliki presisi kurang dari register, menyebabkan perbandingan gagal.float
nilai tanpa presisi ekstra.int a=1; int b=a; assert( a==b );
pernyataan, saya pikir itu hanya masuk akal untuk menjawab pertanyaan ini sehubungan dengan kompiler yang berfungsi dengan baik (sementara mungkin mencatat bahwa beberapa versi dari beberapa kompiler melakukan / memiliki -Dapat diketahui-untuk mendapatkan ini salah). Dalam istilah praktis, jika karena alasan tertentu kompiler tidak menghapus presisi ekstra dari hasil tugas register-disimpan, ia harus melakukannya sebelum menggunakan nilai itu.Ya, itu akan selalu mengembalikan True , kecuali jika itu NaN . Jika nilai variabel NaN maka selalu mengembalikan False !
sumber