# Spot the defect: rounding

The intention of this method is to round a double to the nearest integer. If the double is exactly half way between two integers then it rounds to the larger of the two possibilities:[1. For negative numbers, -1.5 should round to -1.0, since -1.0 is larger than -2.0; I do not mean larger in the sense of absolute magnitude. That would be characterized as “midpoint rounding away from zero”.]

```static double MyRound(double d)
{
return Math.Floor(d + 0.5);
}```

Is it correct? Can you find a value for which it does not give the mathematically correct value?[2. HINT: The value I’m thinking of is small.]

Next time on FAIC: The answer, of course.

## 32 thoughts on “Spot the defect: rounding”

1. RichB on said:

Epsilon

• OK, give more details; what would epsilon round to, and why is that wrong?

• RichB on said:

bah. forget

2. Dude on said:

minus epsilon woud round up to 1 instead of 0?

3. 0.5 – Double.Epsilon should round to 0 but with this algorithm rounds to 1.

• Rik on said:

-0.5 – double.Epsilon rounds to 0

• Rik on said:

(0.5-Double.Epsilon == 0.5) evaluates to true, so maybe this is cheating.

4. Patrick McDonald on said:

0.5 – (some number close to machine epsilon, don’t know the terminology)

var machineEpsilon = 1.0;
while (1.0 + machineEpsilon > 1)
machineEpsilon /= 2;

MyRound(0.5 – machineEpsilon / 2) returns 1;

• Patrick McDonald on said:

Specifically
MyRound(0.49999999999999994);

• You got it!

Man, that was fast.

5. Anton on said:

Any number in between 0.5 and 0.49999999999999999 (which is 0.5 – 1e-17) results in 1 instead of 0. The same for 0.5 – double.Epsilon

• Patrick McDonald on said:

var x = 0.5 – double.Epsilon;
if (x == 0.5)
Console.WriteLine(“Huh? What’s going on here?”);

6. The number is 0.5 – ε/4, aka. the largest floating-point number strictly less than 0.5. Explanation: ε/4 is the unit of the last place of numbers between 0.25 (inclusive) and 0.5 (exclusive). When you add 0.5 to 0.5 – ε/4, you get 1.0 – ε/4 in exact arithmetic. However, this number has too many bits. The last bit is a 1, so round-to-even means the number as a whole is rounded to 1. The function returns 1, which is not correct.

• Exactly right; nice explanation.

7. Stilgar on said:

In my opinion this question is pointless in practice. Floating point numbers should not be used for exact calculations because they do not represent exact numbers. So if the number passed as argument is not exact by definition then how do we know that the rounding is not correct?

• I totally agree; in practice we’re making a decision based on one part in two to the fifty-something, which is an absurdly small quantity. The exercise here is simply encouraging people to think about how floating point math differs from regular math in subtle ways.

• Interesting post, thanks.

It’s a coincidence; I don’t read that blog.

• Brian on said:

In that post, they demonstrate a more extreme bug wherein myround(8388609.0f) returns 8388610.0f . This does not happen in the article’s code, though.

• Random832 on said:

Only because this code works with a double. You can definitely construct an value that it does happen with. Try 4503599627370497. This value happens to be 0x10000000000001, the second value to require 53 bits to represent, just as the one given on that page is 0x800001, the second value to require 24 bits to represent.

8. Anonymous coward on said:

There are other problematic values, e.g.
MyRound (-163840000000001.5) == -163840000000001.0
Of course the source of the problem is the same.

• Anonymous coward on said:

The perils of decimal notation. Sorry for mistaken post.

• Joel Rondeau on said:

There are still other problematic values. I was wondering about the other end of the spectrum, where consecutive double values are whole numbers.

MyRound(4503599627370497)

returns 4503599627370498.

This is a problem for all odd numbers from 4503599627370497 up to 9007199254740991 (note, I only checked 3 of them).

9. John Payson on said:

A related question to this: are there any values of `float` where `float.Parse` will yield a value which is not the closest possible `float` value to the mathematically-correct one [i.e. there is some other `float` value which isn’t merely tied with `float.Parse`, but is actually closer]? Indeed there are, and for similar reasons to the “rounding” error here. The `float.Parse` method works by converting the input string to `double` and then rounding that. An input value which, before rounded to fit a `double`, might be closer to one `float` value than another, but after rounding to `double` would be equidistant, so the succeeding round to `float` might not pick the value that was closest to the original.

10. Simon on said:

But what are these damn floating point numbers useful for then?

• Chris B on said:

Puzzle building on blogs comes to mind…and confusing non-mathematicians.

11. Anarchymedes on said:

‘If the double is exactly half way between two integers then it rounds to the larger of the two possibilities’. Surely, that means, …rounds to the larger by the absoulte value?’ As far as I remember, Math.Floor() will round, say, -7.64 to -8, which is smaller, not larger.

12. Anarchymedes on said:

Sorry, forget my previous comment: that was mentioned in the footnote 1. 🙂

• Random832 on said:

The .NET definition of epsilon is strange. Most other systems define it as the smallest number where 1+ϵ != 1, instead – which is, needless to say, a much larger number – 2^-52 for double.

• 2^52, the value many languages define DBL_EPSILON as, is not quite “the smallest number where 1+ϵ != 1”, not in the default round-to-nearest mode anyway.

What these languages define DBL_EPSILON as is the difference between the smallest double strictly above 1.0 and 1.0.

There is a factor of nearly 2 between the two.

13. JustAPerson on said:

Just wondering, wouldn’t double.MaxValue fail this? (Unless you wrapped with checked, of course).