Inilah yang terjadi dalam biner. Seperti yang kita ketahui, beberapa nilai floating-point tidak dapat direpresentasikan secara tepat dalam biner, bahkan jika mereka dapat direpresentasikan secara tepat dalam desimal. 3 angka ini hanyalah contoh dari fakta itu.
Dengan program ini saya menampilkan representasi heksadesimal dari setiap angka dan hasil dari setiap penambahan.
public class Main{
public static void main(String args[]) {
double x = 23.53; // Inexact representation
double y = 5.88; // Inexact representation
double z = 17.64; // Inexact representation
double s = 47.05; // What math tells us the sum should be; still inexact
printValueAndInHex(x);
printValueAndInHex(y);
printValueAndInHex(z);
printValueAndInHex(s);
System.out.println("--------");
double t1 = x + y;
printValueAndInHex(t1);
t1 = t1 + z;
printValueAndInHex(t1);
System.out.println("--------");
double t2 = x + z;
printValueAndInHex(t2);
t2 = t2 + y;
printValueAndInHex(t2);
}
private static void printValueAndInHex(double d)
{
System.out.println(Long.toHexString(Double.doubleToLongBits(d)) + ": " + d);
}
}
The printValueAndInHex
Metode ini hanya pembantu hex-printer.
Outputnya adalah sebagai berikut:
403787ae147ae148: 23.53
4017851eb851eb85: 5.88
4031a3d70a3d70a4: 17.64
4047866666666666: 47.05
--------
403d68f5c28f5c29: 29.41
4047866666666666: 47.05
--------
404495c28f5c28f6: 41.17
4047866666666667: 47.050000000000004
4 angka pertama adalah x
, y
, z
, dan s
's representasi heksadesimal. Dalam representasi floating point IEEE, bit 2-12 mewakili eksponen biner , yaitu skala angka. (Bit pertama adalah bit tanda, dan bit yang tersisa untuk mantissa .) Eksponen yang diwakili sebenarnya adalah angka biner minus 1023.
Eksponen untuk 4 angka pertama diekstraksi:
sign|exponent
403 => 0|100 0000 0011| => 1027 - 1023 = 4
401 => 0|100 0000 0001| => 1025 - 1023 = 2
403 => 0|100 0000 0011| => 1027 - 1023 = 4
404 => 0|100 0000 0100| => 1028 - 1023 = 5
Set tambahan pertama
Angka kedua ( y
) besarnya lebih kecil. Saat menambahkan dua angka ini untuk mendapatkan x + y
, 2 bit terakhir dari angka kedua ( 01
) digeser keluar dari jangkauan dan tidak termasuk dalam perhitungan.
Penambahan kedua menambah x + y
dan z
dan menambahkan dua angka dari skala yang sama.
Set tambahan kedua
Di sini, x + z
terjadi dulu. Mereka memiliki skala yang sama, tetapi mereka menghasilkan angka yang lebih tinggi dalam skala:
404 => 0|100 0000 0100| => 1028 - 1023 = 5
Penambahan kedua menambahkan x + z
dan y
, dan sekarang 3 bit dijatuhkan dari y
untuk menambahkan angka ( 101
). Di sini, harus ada putaran ke atas, karena hasilnya adalah angka floating point berikutnya: 4047866666666666
untuk set tambahan pertama vs 4047866666666667
untuk set tambahan kedua. Kesalahan itu cukup signifikan untuk ditampilkan dalam cetakan total.
Kesimpulannya, berhati-hatilah saat melakukan operasi matematika pada nomor IEEE. Beberapa representasi tidak tepat, dan mereka menjadi lebih tidak eksak ketika skalanya berbeda. Tambahkan dan kurangi angka dengan skala yang sama jika Anda bisa.
(2.0^53 + 1) - 1 == 2.0^53 - 1 != 2^53 == 2^53 + (1 - 1)
). Karenanya, ya: berhati-hatilah saat memilih urutan jumlah dan operasi lainnya. Beberapa bahasa menyediakan built-in untuk melakukan penjumlahan "presisi tinggi" (misalnya pythonmath.fsum
), jadi Anda dapat mempertimbangkan untuk menggunakan fungsi-fungsi ini alih-alih algoritma jumlah naif.