Home > OS >  Given an `int A` Is there a strong guarantee that `A == (int) (double) A`?
Given an `int A` Is there a strong guarantee that `A == (int) (double) A`?

Time:06-15

The problem is this:

I need strong guarantee that int x = (int) std::round(y) will give always the correct results (y is finite and "humanly" e.g. -50000 to 50000).

std::round(4.1) can give 4.000000000001 or 3.99999999999. In later case, casting to int gives 3 right?

To do so, I reinvent the wheel with this ungly function:

template<std::integral S = int, std::floating_point T>
S roundi(T x)
{
    S r = (S) x;
    T r2 = std::fmod(x, 1);
    if (r2 >= 0.5) return r   1;
    if (r2 <= -0.5) return r - 1;
    return r;
}

But is this necessary? Or casting from double to int uses last mantissa bit for rounding?

CodePudding user response:

Assuming int is 32 bits wide and double is 64 bits wide (and assuming IEEE 754), all values of int are exactly representable in a double.

That means std::round(4.1) returns exactly 4. Nothing more nothing less. And casting that number to int is always 4 exactly.

CodePudding user response:

std::round(4.1) can give 4.000000000001 or 3.99999999999. In later case, casting to int gives 3 right?

That's true. 4.1 can be seen as 4.0 (which has exact representation in floating point as an integer it is) plus 0.1, which can be seen as 1/10 (it's exactly 1/10, indeed) And the problem you will have is if you try to round a number close to that to one decimal point after the decimal mark (rounding to an integer multiple of 0.1 or 0.01 or 0.001, etc.)

If you are using decimal floating point (which normally C compilers don't) then you are lucky, as 0.1 is 10&^(-1) which again has an exact representation in the machine. But as a binary floating point number, it has an infinite representation in binary as 0.000110011001100110011001100...b and it depends where you cut the number you will get some value or another, but you will never get the exact value as a decimal number (with a finite number of digits)

But the way round() works is not that... if first adds 0.5 (which is exactly representable as a binary floating point number) to the number (this results in an exact operation, no rounding error emerges from it), and then cuts the integer part (which is also an exact operation), meaning that you are getting always an exact integer result (which is perfectly representable as an exact floating point, if the original number was). The rounding is equivalent to this set of operations:

(int)(4.1   0.5);

so you will get the integer part of 4.6 after addding the 0.5 part (or something like 4.60000000000000003, 4.59999999999999998, anyway both will be truncated to 4.0, which is also exactly representable in binary floating point format) so you will never get a wrong answer for the rounding to integer case... you can get a wrong response in case you get something close to 4.5 (which can round to 4.0 instead of the correct rounding to 5.0, but .5 happens to be exactly 0.1b in binary... and so it's not affected --

Beware although that rounding to multiples of a negative power of ten (0.1, 0.01, ...) is not warranted, as none of those numbers is representable exactly in binary floating point. All of them have an infinite representation as binary numbers, and due to the cutting at some point, they can be represented as a tiny number above or below (depending on which is close) and the rounding will not work.

CodePudding user response:

std::round(4.1) can give 4.000000000001 or 3.99999999999. In later case, casting to int gives 3 right?

No, it cannot. The result of std::round is always an integer, exactly, with no rounding error.

I need strong guarantee that int x = (int) std::round(y) will give always the correct results (y is finite and "humanly" e.g. -50000 to 50000).

C inherits its floating-point model from C, and, per C 2018 5.2.4.2.2 12, double is capable of representing at least ten-digit integers, so [−50,000, 50,000] is well within its range. It is even within the range of float, which is capable of representing six-digit integers. This requirement extends back to C 1990.

Given an int A Is there a strong guarantee that A == (int) (double) A?

No, the C standard does not impose an upper limit on the width of int nor a relationship between with precision of int (number of bits it uses for the value, excluding the sign bit) and the precision of double (number of bits or other digits in its significand), so a C implementation may have an int with more precision than double.

  •  Tags:  
  • c
  • Related