Does Casting to an Int After Std::Floor Guarantee the Right Result

Does casting to an int after std::floor guarantee the right result?

The range of double is way greater than the range of 32 or 64 bit integers, which is why std::floor returns a double. Casting to int should be fine so long as it's within the appropriate range - but be aware that a double can't represent all 64 bit integers exactly, so you may also end up with errors when you go beyond the point at which the accuracy of double is such that the difference between two consecutive doubles is greater than 1.

Does casting `std::floor()` and `std::ceil()` to integer type always give the correct result?

People often get the impression that floating point operations produce results with small, unpredictable, quasi-random errors. This impression is incorrect.

Floating point arithmetic computations are as exact as possible. 18/3 will always produce exactly 6. The result of 1/3 won't be exactly one third, but it will be the closest number to one third that is representable as a floating point number.

So the examples you showed are guaranteed to always work. As for your suggested "guaranteed floor/ceil", it's not a good idea. Certain sequences of operations can easily blow the error far above 1e-10, and certain other use cases will require 1e-10 to be correctly recognized (and ceil'ed) as nonzero.

As a rule of thumb, hardcoded epsilon values are bugs in your code.

Efficient integer floor function in C++

Have a look at magic numbers. The algorithm proposed on the web page should be far more efficient than simple casting. I've never used it myself, but this is the performance comparison they offer on the site (xs_ToInt and xs_CRoundToInt are the proposed functions):

Performing 10000000 times:
simple cast 2819 ms i.e. i = (long)f;
xs_ToInt 1242 ms i.e. i = xs_ToInt(f); //numerically same as above
bit-twiddle(full) 1093 ms i.e. i = BitConvertToInt(f); //rounding from Fluid
fistp 676 ms i.e. i = FISTToInt(f); //Herf, et al x86 Assembly rounding
bit-twiddle(limited) 623 ms i.e. i = FloatTo23Bits(f); //Herf, rounding only in the range (0...1]
xs_CRoundToInt 609 ms i.e. i = xs_CRoundToInt(f); //rounding with "magic" numbers

Further, the xs_ToInt is apparently modified so that the performance improves:

Performing 10000000 times:
simple cast convert 3186 ms i.e. fi = (f*65536);
fistp convert 3031 ms i.e. fi = FISTToInt(f*65536);
xs_ToFix 622 ms i.e. fi = xs_Fix<16>::ToFix(f);

Brief explanation of how the 'magic numbers' method works:

"Basically, in order to add two floating point numbers, your processor "lines up" the decimal points of the numbers so that it can easily add the bits. It does this by "normalizing" the numbers such that the most significant bits are preserved, i.e. the smaller number "normalizes" to match the bigger one. So the principle of the "magic number" conversion that xs_CRoundToInt() uses is this: We add a big enough floating point number (a number that is so big that there are significant digits only UP TO the decimal point, and none after it) to the one you're converting such that: (a) the number gets normalized by the processor to its integer equivalent and (b) adding the two does not erase the integral significat bits in the number you were trying to convert (i.e. XX00 + 00YY = XXYY)."

The quote is taken from the same web page.

Floor, int conversion is not working correctly for large values

This is the nature of limited-precision representations.

For example, consider six decimal digits of precision. This is not what double uses, of course, but the concept is precisely the same. 1/3 is .333333 with six digits of decimal precision. So 30 * (1/3) is 9.99999. If you round down to an integer, you get 9, not 10. This is the problem you are seeing.

If you don't want this behavior, don't use floating point.

It may suffice to add a small "delta" to the value you pass to floor.

Can I trust a real-to-int conversion of the result of ceil()?

You may want to write an explicit function for your case. e.g.:

/* return the smallest positive integer whose square is at least x */
int isqrt(double x) {
int y1 = ceil(sqrt(x));
int y2 = y1 - 1;
if ((y2 * y2) >= x) return y2;
return y1;
}

This will handle the odd case where the square root of your ratio a/b is within the precision of double.

How can I trust casting from double to integer?

In the comments you say that you want the Integer part of the result. Well, unfortunately the double result of 1.2 / 0.4 just happens to be 2.9999999999999996 (On my machine. You can see the exact value with cout by using std::setprecision) and therefore the integer part of the result is 2. This, as you know, is because not all numbers can be represented with floating point numbers and because floating point operations incur errors.

Taking the integer part of a floating point number is on the same level as comparing floating point numbers with equality; You are not going to get consistent results. If you must have exact results, the general solution is to not use floating point numbers at all, but fixed point instead.

As with the equality comparison, you can work around the issue with an appropriate epsilon value. In this case you can add (or subtract if negative) a very small floating point number to the result before taking the integer part. The added number must be larger than the largest possible error the number might have but smaller than the smallest precision number that you must support (so that 9.999 doesn't become 10 if you must support down to 0.001 precision). Figuring out a good number for this can be quite hard.

Comparing floats/doubles bitwise after floor/ceil

fabs(floor(a) - floor(b)) < 0.0001 only if floor(a) == floor(b), as long as you're in the range of integers that doubles can accurately represent: Does casting to an int after std::floor guarantee the right result?

Why do round() and ceil() not return an integer?

The integral value returned by these functions may be too large to store in an integer type (int, long, etc.). To avoid an overflow, which will produce undefined results, an application should perform a range check on the returned value before assigning it to an integer type.

from the ceil(3) Linux man page.



Related Topics



Leave a reply



Submit