# The Wiert Corner – irregular stream of stuff

• ## Email Subscription

Join 2,574 other followers

## Why is the result of RoundTo(87.285, -2) => 87.28 – Stack Overflow

Posted by jpluimers on 2011/03/08

Programmers on all sorts of platforms get this wrong all the time (I admit having done this in bad ways myself too).

In short: Don’t expect floating point values in a computer to be represented as decimals.

Rob Kennedy wrote a very nice answer on this:

The exact value 87.285 is not representable as a floating-point value in Delphi. A page on my Web site shows what that value really is, as Extended, Double, and Single:

`87.285 = + 87.28500 00000 00000 00333 06690 73875 46962 12708 95004 27246 09375`

`87.285 = + 87.28499 99999 99996 58939 48683 51519 10781 86035 15625`

`87.285 = + 87.28500 36621 09375`

And David Heffernan points to the best link you can get on this topic:

The classic reference on floating point is What Every Computer Scientist Should Know About Floating-Point Arithmetic.

For currency based calculations, if indeed this is, you should use a base 10 number type rather than base 2 floating point. In Delphi that means `Currency`.

–jeroen

[…] […]

2. ### Louis Kleimansaid

I was just reading the help:

From the RAD Studio 2010 Help on RoundTo():

RoundTo uses “Banker’s Rounding” to determine how to round values that are exactly midway between the two values that have the desired number of significant digits. This method rounds to an even number in the case that AValue is not nearer to either value.

The following examples illustrate the use of RoundTo:
Expression Value
RoundTo(1234567, 3) 1235000
RoundTo(1.234, -2) 1.23
RoundTo(1.235, -2) 1.24
RoundTo(1.245, -2) 1.24

Note: The behavior of RoundTo can be affected by the Set8087CW procedure or SetRoundMode function.

OK, but I am wrong. Thanks for straightening me out. I trusted the letter of the help at the surface without looking deeper.

Just as a side note, SetRoundMode appears to have no effect on the results of the example David put out.

Thanks, everyone. I’ll go back into my corner now…

Don’t worry we’re not mad at you ;-)

Please file a QC bug report (at http://qc.embarcadero.com) on the documentation issue; you are totally right that it gives you the wrong impression.

–jeroen

• ### David Heffernansaid

Banker’s rounding could only be relevant if the routine was using a decimal data type rather than a binary one. SetRoundMode appears to be irrelevant because RoundTo sets the control word. It does indeed appear to be a documentation bug. Older versions of Delphi used a very different implementation of RoundTo. I’m looking at D6 for example and the implementation is different, will be affected by round mode, doesn’t appear to do banker’s rounding, and has the same documentation. Who knew?

3. ### Louis Kleimansaid

Come on, folks, read the documentation. The answer to this has nothing to do with inexact representation of floating point numbers and everything to do with the fact that RoundTo() ALWAYS rounds a 5 to the nearest EVEN digit.

It doesn’t; it depens on the rounding mode. Lot’s of DLLs even explicitly twiddle with the rounding mode (and other floating point settings) in an unwanted way.
–jeroen

• ### David Heffernansaid

@Louis

var
d: Double;

d := 87.205; Writeln(RoundTo(d, -2));
d := 87.215; Writeln(RoundTo(d, -2));
d := 87.225; Writeln(RoundTo(d, -2));
d := 87.235; Writeln(RoundTo(d, -2));
d := 87.245; Writeln(RoundTo(d, -2));
d := 87.255; Writeln(RoundTo(d, -2));
d := 87.265; Writeln(RoundTo(d, -2));
d := 87.275; Writeln(RoundTo(d, -2));
d := 87.285; Writeln(RoundTo(d, -2));
d := 87.295; Writeln(RoundTo(d, -2));

Ouput:

8.72000000000000E+0001
8.72200000000000E+0001
8.72200000000000E+0001
8.72300000000000E+0001
8.72500000000000E+0001
8.72500000000000E+0001
8.72700000000000E+0001
8.72800000000000E+0001
8.72800000000000E+0001
8.73000000000000E+0001

How does that fit into your banker’s rounding theory?

• ### Erik Knowlessaid

I think we’re all familiar with Gaussian rounding. However, this particular result is not the result of said rounding mode; in fact, why don’t you take your own advice and read the top of *this* webpage? Specifically: 8.285 as a double is: “87.28499 99999 99996 58939 48683 51519 10781 86035 15625”. Banker’s rounding doesn’t apply.

4. ### Jolyon Smithsaid

Slightly disappointing to note that this long established fact of floating points on current hardware STILL keeps cropping up.

Just WHAT are they teaching in computer science class these days ?!? :)

On a side note, the post title was confusing to me… it read to me as “RoundTo(87.285, -2) equals some value greater than 87.28” (the more mathematically ‘correct’ reading of the => symbol as “implies” just didn’t make *any* sense) … why not just write it as “=” (equals), which is actually the point. … It was just a tad confusing, tho no real biggy.

:)

5. ### Erik Knowlessaid

Second the “What every computer scientist should know…” endorsement. It takes quite a bit of digesting to get past the author’s tendency towards handwaving, but it’s well worth the study.

6. ### Louis Kleimansaid

Just a minor issue — currency isn’t a “base 10 number type”. As I understand it, a currency value is an integer type internally, but the decimal point is shifted 4 places for you. And I believe that RoundTo() uses “banker’s rounding” which means round to the nearest even number when a 5 is encountered.

You are right; Currency is accurate till 4 decimals because it has an implied decimal point.

Rounding depends on how you setup the floating point unit using the control word.
Rudy Velthuis has some excellent articles on that: http://rvelthuis.de/articles/articles-floats.html

–jeroen

• ### David Heffernansaid

The scaling by a power of 10 makes Currency behave as a decimal rather than a binary type. For example the number in question, 87.285 is exactly representable in Currency, but not in base-2 floating point. That was the point I was trying to make.

This site uses Akismet to reduce spam. Learn how your comment data is processed.