W jaki sposób printf zaokrągla połówki do pierwszego miejsca po przecinku?

11

Testuję dwie różne implementacje printfw moim systemie: printf (GNU coreutils) 8.26oraz wersję w pakiecie zsh 5.3.1. Testuję, jak zaokrąglane są połówki liczb, tj. Dla 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

Tutaj oba są wyraźnie zaokrąglone do połowy . Jednak gdy testuję zaokrąglanie do pierwszego miejsca po przecinku, sprawy stają się mylące. To znaczy, testuję na 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

Obie implementacje robią to inaczej i nie widzę w nich żadnego wyraźnego wzorca. Jak te dwie printfokrągłe połowy dzielą na pierwsze miejsce po przecinku?

Krogulec
źródło

Odpowiedzi:

19

GNU printf używa,long double podczas gdy zsh używa zwykłych doubles . Zaokrąglanie, które widzisz, jest spowodowane tym, że (powiedzmy) 1,45 nie może być reprezentowane jako suma potęg 2, tak działa reprezentacja zmiennoprzecinkowa IEEE 754 , a najbliższe przybliżenie zmienia się w zależności od precyzji. To nieco więcej (z 80 bitami) lub mniej (z 64 bitami), a więc zaokrąglanie w górę lub w dół, jak widać.

Jak zawsze, jeśli zależy Ci na dokładnej reprezentacji na poziomie człowieka i zaokrąglaniu, nie używaj liczb zmiennoprzecinkowych.

Michael Homer
źródło
1
Wystarczy zauważyć, że x.25 i x.75 (dla odpowiednio małego x) mają dokładne reprezentacje binarne, w przeciwieństwie do innych.
Toby Speight