JavaScript sering diejek apabila pembangun mula-mula menemui hasil yang kelihatan membingungkan ini:
0.1 + 0.2 == 0.30000000000000004
Meme tentang pengendalian nombor JavaScript tersebar luas, sering menyebabkan ramai percaya bahawa tingkah laku ini unik kepada bahasa tersebut.
Walau bagaimanapun, kebiasaan ini bukan sahaja terhad kepada JavaScript. Ini adalah akibat daripada cara kebanyakan bahasa pengaturcaraan mengendalikan aritmetik titik terapung.
Sebagai contoh, berikut ialah coretan kod daripada Java dan Go yang menghasilkan hasil yang serupa:
Komputer secara asli hanya boleh menyimpan integer. Mereka tidak faham pecahan. (Bagaimanakah mereka? Satu-satunya cara komputer boleh melakukan aritmetik ialah dengan menghidupkan atau mematikan beberapa lampu. Lampu boleh sama ada dihidupkan atau dimatikan. Ia tidak boleh "separuh" menyala!) Mereka memerlukan beberapa cara untuk mewakili nombor titik terapung . Oleh kerana perwakilan ini tidak tepat dengan sempurna, lebih kerap daripada tidak, 0.1 0.2 tidak sama dengan 0.3.
Semua pecahan yang penyebutnya terdiri daripada faktor perdana asas sistem nombor boleh dinyatakan dengan bersih manakala mana-mana pecahan lain akan mempunyai perpuluhan berulang. Sebagai contoh, dalam sistem nombor dengan asas 10, pecahan seperti 1/2, 1/4, 1/5, 1/10 diwakili dengan jelas kerana penyebut dalam setiap kes terdiri daripada 2 atau 5 - faktor perdana 10 . Bagaimanapun, pecahan seperti 1/3, 1/6, 1/7 semuanya mempunyai perpuluhan berulang.
Begitu juga, dalam pecahan sistem binari seperti 1/2, 1/4, 1/8 dinyatakan dengan bersih manakala semua pecahan lain mempunyai perpuluhan berulang. Apabila anda melakukan aritmetik pada perpuluhan berulang ini, anda akan mendapat sisa yang terbawa apabila anda menukar perwakilan binari nombor komputer kepada perwakilan asas-10 yang boleh dibaca manusia. Inilah yang membawa kepada hasil yang lebih kurang betul.
Sekarang kami telah menetapkan bahawa masalah ini bukan eksklusif untuk JavaScript, mari kita terokai cara nombor titik terapung diwakili dan diproses di bawah hud untuk memahami sebab gelagat ini berlaku.
Untuk memahami cara nombor titik terapung diwakili dan diproses di bawah hud, kami perlu memahami piawaian titik terapung IEEE 754 terlebih dahulu.
Piawaian IEEE 754 ialah spesifikasi yang digunakan secara meluas untuk mewakili dan melaksanakan aritmetik pada nombor titik terapung dalam sistem komputer. Ia dicipta untuk menjamin ketekalan apabila menggunakan aritmetik titik terapung pada pelbagai platform pengkomputeran. Kebanyakan bahasa pengaturcaraan dan pelaksanaan perkakasan (CPU, GPU, dll.) mematuhi standard ini.
Beginilah cara nombor dilambangkan dalam format IEEE 754:
Di sini s ialah bit tanda (0 untuk positif, 1 untuk negatif), M ialah mantissa (memegang digit nombor) dan E ialah eksponen yang menentukan skala nombor.
Anda tidak akan dapat mencari sebarang nilai integer untuk M dan E yang betul-betul boleh mewakili nombor seperti 0.1, 0.2 atau 0.3 dalam format ini. Kami hanya boleh memilih nilai untuk M dan E yang memberikan hasil yang paling hampir.
Berikut ialah alat yang boleh anda gunakan untuk menentukan Notasi IEEE 754 nombor perpuluhan: https://www.h-schmidt.net/FloatConverter/IEEE754.html
IEEE 754 tatatanda 0.25:
IEEE 754 tatatanda 0.1 dan 0.2 masing-masing:
Sila ambil perhatian bahawa ralat disebabkan penukaran dalam kes 0.25 ialah 0, manakala 0.1 dan 0.2 mempunyai ralat bukan sifar.
IEEE 754 mentakrifkan format berikut untuk mewakili nombor titik terapung:
Ketepatan tunggal (32-bit): 1 bit untuk tanda, 8 bit untuk eksponen, 23 bit untuk mantissa
Ketepatan dua kali (64-bit): 1 bit untuk tanda, 11 bit untuk eksponen, 52 bit untuk mantissa
Demi kesederhanaan, mari kita pertimbangkan format ketepatan tunggal yang menggunakan 32 bit.
Perwakilan 32 bit 0.1 ialah:
0 01111011 10011001100110011001101
Here the first bit represents the sign (0 which means positive in this case), the next 8 bits (01111011) represent the exponent and the final 23 bits (10011001100110011001101) represent the mantissa.
This is not an exact representation. It represents ≈ 0.100000001490116119384765625
Similarly, the 32 bit representation of 0.2 is:
0 01111100 10011001100110011001101
This is not an exact representation either. It represents ≈ 0.20000000298023223876953125
When added, this results in:
0 01111101 11001101010011001100110
which is ≈ 0.30000001192092896 in decimal representation.
In conclusion, the seemingly perplexing result of 0.1 + 0.2 not yielding 0.3 is not an anomaly specific to JavaScript, but a consequence of the limitations of floating-point arithmetic across programming languages. The roots of this behaviour lie in the binary representation of numbers, which inherently leads to precision errors when handling certain fractions.
Atas ialah kandungan terperinci Di luar JavaScript - Mengapa tidak sama dalam pengaturcaraan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!