Как printf округляет половинки до первого знака после запятой?

11

Я тестирую две разные реализации в printfмоей системе: printf (GNU coreutils) 8.26и версию в комплекте zsh 5.3.1. Я проверяю, как округляются половинные числа, то есть для 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

Здесь оба четко округлены до половины . Однако, когда я проверяю округление до первого знака после запятой, все становится запутанным. То есть я тестирую на 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

Обе реализации делают это по-разному, и я не вижу четкой картины в них. Как эти две printfкруглые половины с первого десятичного знака?

Sparhawk
источник

Ответы:

19

GNU printf использует, вlong double то время как zsh использует обычные doubles . Поведение округления, которое вы видите, заключается в том, что (скажем) 1.45 нельзя представить в виде суммы степеней 2, как работает представление IEEE 754 с плавающей запятой , а ближайшее приближение изменяется в зависимости от точности. Это немного больше (с 80 битами) или меньше (с 64 битами), округляя, как видите, вверх или вниз.

Как всегда, если вы заботитесь о точном представлении и округлении на уровне человека, не используйте с плавающей точкой.

Майкл Гомер
источник
1
Просто обратите внимание, что x.25 и x.75 (для достаточно малого x) имеют точные двоичные представления, в отличие от других.
Тоби Спейт