[OT] Fun with printf (rounding)

24 views
Skip to first unread message

Albrecht Schlosser

unread,
Mar 25, 2021, 7:56:33 PM3/25/21
to fltk.coredev
FYI - this is absolutely OT.

$ cat round.c

(attached)

$ cc -o round round.c && ./round

f = 1.0/8.0 = 0.125 = 0.12 = 0.1 = 0, 0.1 + 1 = 1
f = 2.0/8.0 = 0.250 = 0.25 = 0.2 = 0, 0.2 + 1 = 1
f = 3.0/8.0 = 0.375 = 0.38 = 0.4 = 0, 0.4 + 1 = 1
f = 4.0/8.0 = 0.500 = 0.50 = 0.5 = 0, 0.5 + 1 = 2
f = 5.0/8.0 = 0.625 = 0.62 = 0.6 = 1, 0.6 + 1 = 2
f = 6.0/8.0 = 0.750 = 0.75 = 0.8 = 1, 0.8 + 1 = 2
f = 7.0/8.0 = 0.875 = 0.88 = 0.9 = 1, 0.9 + 1 = 2
f = 8.0/8.0 = 1.000 = 1.00 = 1.0 = 1, 1.0 + 1 = 2
f = 9.0/8.0 = 1.125 = 1.12 = 1.1 = 1, 1.1 + 1 = 2
f = 10.0/8.0 = 1.250 = 1.25 = 1.2 = 1, 1.2 + 1 = 2
f = 11.0/8.0 = 1.375 = 1.38 = 1.4 = 1, 1.4 + 1 = 2
f = 12.0/8.0 = 1.500 = 1.50 = 1.5 = 2, 1.5 + 1 = 2
f = 13.0/8.0 = 1.625 = 1.62 = 1.6 = 2, 1.6 + 1 = 3
f = 14.0/8.0 = 1.750 = 1.75 = 1.8 = 2, 1.8 + 1 = 3
f = 15.0/8.0 = 1.875 = 1.88 = 1.9 = 2, 1.9 + 1 = 3
f = 16.0/8.0 = 2.000 = 2.00 = 2.0 = 2, 2.0 + 1 = 3


Would you have expected *all* these (rounding) "results"?

For instance:

0.25 = 0.2
0.5 + 1 = 2, but: 1.5 + 1 = 2
round.c

Greg Ercolano

unread,
Mar 25, 2021, 8:36:54 PM3/25/21
to fltkc...@googlegroups.com

On 3/25/21 4:56 PM, Albrecht Schlosser wrote:

f =  9.0/8.0 = 1.125 = 1.12 = ..
    ..well I didn't check them all, but I certainly didn't expect /that/ :^D
    I know that floating point rounding can get tricky.. there's some interesting stuff here:
    https://stackoverflow.com/questions/56546110/how-does-printf-in-c-round-floating-point-numbers/56547193
    ..esp in that last comment that links to this page on how rounding is often done
    using dtoa.c, and if that code wasn't gnarly enough, you can go deeper here:
    https://stackoverflow.com/questions/3173056/why-does-dtoa-c-contain-so-much-code

    If you've ever seen the movie "Office Space", written and directed by the guy that brought us
    Beavis and Butthead, I believe this type of thing was one of the core plot lines.

       
   

duncan

unread,
Mar 25, 2021, 8:52:57 PM3/25/21
to fltk.coredev
I was brought up with a single rounding rule, that anything greater or equal to .5 rounded to the nearest whole away from zero, i.e. rounding 1.5 gives 2, and rounding -1.5 gives -2. In my programming life, I've never consciously noticed anything the contradicted this, but maybe I never really noticed.

It came as a surprise to me while taking a distance learning maths course that there are different rules about rounding, depending on conttext, snd summarized at https://en.wikipedia.org/wiki/Rounding

So, knowing that there are different rounding strategies out there, it's not a surprise, but knowing that this is about a deterministic programming language result, it is a big surprise.

D.

Greg Ercolano

unread,
Mar 25, 2021, 9:23:53 PM3/25/21
to fltkc...@googlegroups.com


On 3/25/21 5:52 PM, duncan wrote:
I was brought up with a single rounding rule, that anything greater or equal to .5 rounded to the nearest whole away from zero, i.e. rounding 1.5 gives 2, and rounding -1.5 gives -2. In my programming life, I've never consciously noticed anything the contradicted this, but maybe I never really noticed.

    I think the weird zone is where, in the case I cited of 9/8=1.125, I think because it's
    /exactly/ 1.1250000000, they didn't round to 1.13. But had it been just a tiny bit over,
    e.g. 1.125001 they would have rounded up.

    I think the guys in the movie "Office Space" got rich playing with these hard to track
    "fractions of a cent" in the accounting software their company wrote, sweeping the
     fractions into their bank account, and being surprised when they ended up with
     a lot of money in a small time.. a plot which I know was based on something that
     was in the news around the time the movie was being made.

Albrecht Schlosser

unread,
Mar 25, 2021, 9:30:53 PM3/25/21
to fltkc...@googlegroups.com
On 3/26/21 1:52 AM duncan wrote:
> I was brought up with a single rounding rule, that anything greater or
> equal to .5 rounded to the nearest whole away from zero, i.e. rounding
> 1.5 gives 2, and rounding -1.5 gives -2. In my programming life, I've
> never consciously noticed anything the contradicted this, but maybe I
> never really noticed.

Yep, I think this is called "commercial rounding" and this is what I
would have expected from printf().

Maybe I should have mentioned that all the calculated numbers in the
demo program are *exact* floating point values. Division by 8 is an
exact (binary) floating operation, so there's no rounding involved in
the calculation.

> It came as a surprise to me while taking a distance learning maths
> course that there are different rules about rounding, depending on
> conttext, snd summarized at https://en.wikipedia.org/wiki/Rounding

Thanks for this link.

And thanks to Greg for his links as well, BTW.

> So, knowing that there are different rounding strategies out there, it's
> not a surprise, but knowing that this is about a deterministic
> programming language result, it is a big surprise.

Unfortunately it is really deterministic but not the usually (by humans)
expected outcome. It came up for me years ago when we used printf() or
maybe sprintf() to convert or output medical measurement values. Besides
real rounding effects our customers did not expect (and accept!) that
3.5 was "rounded" to 4 but 2.5 was "rounded" to 2. We "fixed" it by
writing our own conversion functions.

The "secret" is that *printf() uses the "round half to even" rule which
is absolutely not expected by humans for rounding an *output* value.
https://en.wikipedia.org/wiki/Rounding#Round_half_to_even

If you look at the results of the test program you will notice that each
'.5' will be rounded to the nearest even number instead of the nearest
higher number. This results in "0.125 = 0.12" (unexpected) but "0.875 =
0.88" (expected) and all the other similar effects. At least it's really
deterministic. ;-)

This "round half to even" is explained in the article and it may have
some use but for a single output conversion it's IMHO unexpected.

AFAICT it's useful for "binary rounding" of floating point operations to
minimize *accumulated* rounding effects of the least siginificant bit
(!) as defined in IEEE 754 (see Wikipedia article) but IMHO it's misused
in floating output conversion.

Bill Spitzak

unread,
Mar 25, 2021, 9:37:18 PM3/25/21
to fltkc...@googlegroups.com
Round-to-even has the advantage the the average value of a whole lot of randomly-distributed numbers does not change when you round them all.


--
You received this message because you are subscribed to the Google Groups "fltk.coredev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkcoredev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/fltkcoredev/9c9892ba-d6a9-95dc-9c71-04d8ea858169%40online.de.

Albrecht Schlosser

unread,
Mar 25, 2021, 9:49:11 PM3/25/21
to fltkc...@googlegroups.com
On 3/26/21 2:37 AM Bill Spitzak wrote:
> Round-to-even has the advantage the the average value of a whole lot of
> randomly-distributed numbers does not change when you round them all.

Yes. Nothing against your statement, that's correct.

But my point is that printf() has nothing to do with statistics and
averages. It's just a means to output a single value and I'd expect it
to use /common/ and typically expected rounding mechanisms.

Maybe my expectation is "wrong" but we see that others had the same
expectation.

Greg Ercolano

unread,
Mar 25, 2021, 10:52:24 PM3/25/21
to fltkc...@googlegroups.com
On 3/25/21 6:49 PM, Albrecht Schlosser wrote:
Maybe my expectation is "wrong" but we see that others had the same expectation.

    Speaking for myself, I don't work with floats much in my detailed work,
    so I'm probably not a good data point.

    And when I do, there are a few hard core cases where I couldn't allow any
    floating point weirdness creep in, so I had to make an integer based class
    that managed "floats" the way my application needed, and in particular
    avoided that problem one sometimes gets looping floats where suddenly
    .1 becomes .099999999 due to whatever weirdness is going on in the binary
    representation of floats.

    But yeah, I think the common concept of e.g. rounding floats to integers
    by simply adding .5 / -.5 is kinda common.

    I'm not sure, but I think the printf() mechanism's "rounding mode"
    might be controllable in some cases, e.g. awk/gawk:
    https://www.gnu.org/software/gawk/manual/html_node/Setting-the-rounding-mode.html


Greg Ercolano

unread,
Mar 25, 2021, 11:07:28 PM3/25/21
to fltkc...@googlegroups.com

On 3/25/21 7:52 PM, Greg Ercolano wrote:

    I'm not sure, but I think the printf() mechanism's "rounding mode"
    might be controllable in some cases, e.g. awk/gawk:
    https://www.gnu.org/software/gawk/manual/html_node/Setting-the-rounding-mode.html


    It's funny, that page cites the 0.125 case exactly. Quoting that gawk manual page:

The default mode roundTiesToEven is the most preferred, but the least intuitive. This method does the obvious thing for most values, by rounding them up or down to the nearest digit. [..]

However, when it comes to rounding a value that is exactly halfway between, things do not work the way you probably learned in school. In this case, the number is rounded to the nearest even digit. So rounding 0.125 to two digits rounds down to 0.12, but rounding 0.6875 to three digits rounds up to 0.688.


Reply all
Reply to author
Forward
0 new messages