Printf Math – How Does It Round Halves to the First Decimal Place?

mathprintf

I am testing two different implementations of printf on my system: printf (GNU coreutils) 8.26, and the version bundled with zsh 5.3.1. I am testing how half numbers are rounded, i.e. for 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

Here, both clearly round half to even. However, when I test rounding to the first decimal place, things get confusing. That is, I'm testing for 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

Both implementations do it differently, and I can't see any clear pattern in either. How do these two printfs round halves to the first decimal place?

Best Answer

GNU printf uses long double while zsh uses regular doubles. The rounding behaviour you see is because (say) 1.45 can't be represented as a sum of powers of 2, which is how IEEE 754 floating-point representation works, and the nearest approximation varies according to the precision. It's slightly more (with 80 bits) or less (with 64 bits), thus rounding up or down as you see.

As ever, if you care about accurate human-level representation and rounding, don't use floating-point.