Bagaimana putaran printf membagi dua ke tempat desimal pertama?

11

Saya menguji dua implementasi berbeda printfpada sistem saya:, printf (GNU coreutils) 8.26dan versi yang dibundel dengan zsh 5.3.1. Saya menguji bagaimana angka setengah dibulatkan, yaitu untuk 1,5, 2,5, 3,5, ... 9,5.

$ for i in {1..9}; do /usr/bin/printf '%.0f\n' "${i}.5"; done
2
2
4
4
6
6
8
8
10
$ for i in {1..9}; do printf '%.0f\n' "${i}.5"; done
2
2
4
4
6
6
8
8
10

Di sini, keduanya jelas setengah bulat sampai genap . Namun, ketika saya menguji pembulatan ke tempat desimal pertama, semuanya menjadi membingungkan. Yaitu, saya menguji 1,15, 1,25, 1,35, ... 1,95.

$ for i in {1..9}; do /usr/bin/printf '%.1f\n' "1.${i}5"; done
1.1
1.2
1.4
1.5
1.5
1.6
1.8
1.9
2.0
$ for i in {1..9}; do printf '%.1f\n' "1.${i}5"; done
1.1
1.2
1.4
1.4
1.6
1.6
1.8
1.9
1.9

Kedua implementasi melakukannya secara berbeda, dan saya juga tidak dapat melihat pola yang jelas. Bagaimana kedua printfputaran ini membagi dua ke tempat desimal pertama?

Sparhawk
sumber

Jawaban:

19

GNU printf digunakanlong double sementara zsh menggunakan doubles biasa . Perilaku pembulatan yang Anda lihat adalah karena (katakanlah) 1,45 tidak dapat direpresentasikan sebagai jumlah kekuatan 2, yang merupakan cara representasi titik-mengambang IEEE 754 bekerja, dan perkiraan terdekat bervariasi sesuai dengan presisi. Ini sedikit lebih (dengan 80 bit) atau kurang (dengan 64 bit), sehingga membulatkan ke atas atau ke bawah seperti yang Anda lihat.

Seperti biasa, jika Anda peduli dengan representasi dan pembulatan level manusia yang akurat, jangan gunakan floating-point.

Michael Homer
sumber
1
Hanya sebuah catatan bahwa x.25 dan x.75 (untuk x kecil yang sesuai) memang memiliki representasi biner yang tepat, tidak seperti yang lain.
Toby Speight