The Linux Rain Linux General/Gaming News, Reviews and Tutorials

Eek! My rounding is biased!

By Bob Mesibov, published 07/12/2016 in Tutorials

I've written several BASH scripts that use GNU AWK's printf function to round off decimal numbers. Now I've learned that the rounding is biased, and I need to re-do the scripts.

For me the desirable way to round off a decimal is to round up if the bit to be discarded is greater than one half, and to round down if the bit to be discarded is less than one half. And what's the 'tie-breaker' if the bit to be discarded is exactly one half? If the preceding digit is odd, round up. If the preceding digit is even, do nothing.

In the fairly complicated world of number rounding, my method is called round half to even. It's also called convergent rounding, statistician's rounding, Dutch rounding, Gaussian rounding, odd–even rounding and bankers' rounding.

Here's an example: I'll use BASH printf to round the numbers from 0.10 to 0.29 to a single significant figure after the decimal point:

In the 5 series beginning with 0.00, 0.02, 0.04, 0.06 and 0.08, this rounding method will round up 4 times out of 10, and in the 5 series beginning 0.01, 0.03, 0.05, 0.07 and 0.09 the method will round up 5 times out of 10 [Not true for my BASH printf! See edit below.]. This even splitting of the tie-breaking cases is unbiased, in my view.

Now look what my GNU AWK's printf does. Regardless of whether the preceding digit is odd or even, AWK's printf always rounds up 4 times out of 10:

Why is this? The GNU AWK manual explains:

The way printf and sprintf() ... perform rounding often depends upon the system’s C sprintf() subroutine. On many machines, sprintf() rounding is unbiased, which means it doesn’t always round a trailing .5 up, contrary to naive expectations. In unbiased rounding, .5 rounds to even, rather than always up, so 1.5 rounds to 2 but 4.5 rounds to 4. This means that if you are using a format that does rounding (e.g., "%.0f"), you should check what your system does.

Cleverer people than me could check my 'system’s C sprintf() subroutine' directly. What I know is that on my Debian stable system, BASH does unbiased rounding and AWK does biased rounding. Ugh. What to do?

One workaround would be to complicate the AWK command by adding a test. The modification shown below tests the first digit after the decimal point. If that digit is odd, 0.001 is added to the number to bump up its value. When the last digit is a 5, the bit to be discarded in rounding is bumped up to greater than one half. Note that the AWK command shown is specifically for numbers of the form N.NN, to be rounded to 1 decimal place!

Hmm. I think I'll modify the scripts to use BASH printf...

EDIT I've just discovered that BASH printf is inconsistent: 0.15, 0.55 and 0.75 are rounded up to even, but 0.35 and 0.95 are rounded to odd, and 0.85 is rounded to 0.9. Eek again! Looks like the amended AWK command is the way to go for my uses.

Cat image from cleolinda on Tumblr

About the Author

Bob Mesibov is Tasmanian, retired and a keen Linux tinkerer.

Tags: tutorial scripts rounding printf bash awk posix
blog comments powered by Disqus