Home » C++ » Why do lots of (old) programs use floor(0.5 + input) instead of round(input)? [closed]

Why do lots of (old) programs use floor(0.5 + input) instead of round(input)? [closed]

Posted by: admin November 29, 2017 Leave a comment

Questions:

The differences reside in the returned value giving inputs around tie-breaking I believe, such as this code:

int main()
{
    std::cout.precision(100);

    double input = std::nextafter(0.05, 0.0) / 0.1;
    double x1 = floor(0.5 + input);
    double x2 = round(input);

    std::cout << x1 << std::endl;
    std::cout << x2 << std::endl;
}

which outputs:

1
0

But they are just different results in the end, one chooses its preferred one. I see lots of “old” C/C++ programs using floor(0.5 + input) instead of round(input).

Is there any historic reason? Cheapest on the CPU?

Answers:

std::round is introduced in C++11. Before that, only std::floor was available so programmers were using it.

Questions:
Answers:

There is no historic reason whatsoever. This kind of deviance has been around since year dot. Folk do this when they are feeling very, very naughty. It’s a serious abuse of floating point arithmetic, and many experienced professional programmers fall for it. Even the Java bods did up to version 1.7. Funny guys.

My conjecture is that a decent out-of-the-box German rounding function was not formally available until C++11 (despite C getting theirs in C99), but that really is no excuse for adopting the so-called alternative.

Here’s the thing: floor(0.5 + input) does not always recover the same result as the corresponding std::round call!

The reason is quite subtle: the cutoff point for a German rounding, a.5 for an integer a is, by a coincidental property of the universe, a dyadic rational. As this can be represented exactly in an IEEE754 floating point up to the 52nd power of 2, and thereafter rounding is a no-op anyway, std::round always works properly. For other floating point schemes, consult the documentation.

But adding 0.5 to a double can introduce imprecision causing a slight under or overshoot for some values. If you think about it, adding two double values together – that are the inception of unwitting denary conversions – and applying a function that is a very strong function of the input (such as a rounding function), is bound to end in tears.

Don’t do it.

Reference: Why does Math.round(0.49999999999999994) return 1

Questions:
Answers:

I think this is where you err:

But they are just different results in the end, one chooses its
preferred one. I see lots of “old” C/C++ programs using floor(0.5 +
input) instead of round(input).

That is not the case. You must select the right rounding scheme for the domain. In a financial application, you’ll round using banker’s rules (not using float by the way). When sampling, however, rounding up using static_cast<int>(floor(f + .5)) yields less sampling noise, this increments the dynamic range. When aligning pixels, i.e. converting a position to screen coordinates, using any other rounding method will yield holes, gaps, and other artifacts.

Questions:
Answers:

A simple reason could be that there are different methods of rounding numbers so unless you knew the method used, you could different results.

With floor(), you can be consistent with the results. If the float is .5 or greater, adding it will bump up to the next int. But .49999 will just drop the decimal.

Questions:
Answers:

Many programmers adapt idioms that they learned when programming with other languages. Not all languages have a round() function, and in those languages it’s normal to use floor(x + 0.5) as a substitute. When these programmers start using C++, they don’t always realize that there’s a built-in round(), they continue to use the style they’re used to.

In other words, just because you see lots of code that does something, it doesn’t mean there’s a good reason to do it. You can find examples of this in every programming language. Remember Sturgeon’s Law:

ninety percent of everything is crap