Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check if float can be exactly represented as an integer

Tags:

c

double

ieee-754

I'm looking to for a reasonably efficient way of determining if a floating point value (double) can be exactly represented by an integer data type (long, 64 bit).

My initial thought was to check the exponent to see if it was 0 (or more precisely 127). But that won't work because 2.0 would be e=1 m=1...

So basically, I am stuck. I have a feeling that I can do this with bit masks, but I'm just not getting my head around how to do that at this point.

So how can I check to see if a double is exactly representable as a long?

Thanks

like image 624
ircmaxell Avatar asked Jan 18 '12 04:01

ircmaxell


People also ask

How do you check if a float is an integer?

Check if float is integer: is_integer() float has is_integer() method that returns True if the value is an integer, and False otherwise. For example, a function that returns True for an integer number ( int or integer float ) can be defined as follows. This function returns False for str .

How do you know if a number can be represented in floating-point?

If you have a number of reasonable magnitude with only a few significant digits in its decimal representation, then if it passes this test it is exactly representable.

Can float represent integers?

Real numbers are represented in C by the floating point types float, double, and long double. Just as the integer types can't represent all integers because they fit in a bounded number of bytes, so also the floating-point types can't represent all real numbers.

How do you check if a value is an integer?

The Number. isInteger() method returns true if a value is an integer of the datatype Number. Otherwise it returns false .


1 Answers

Here's one method that could work in most cases. I'm not sure if/how it will break if you give it NaN, INF, very large (overflow) numbers...
(Though I think they will all return false - not exactly representable.)

You could:

  1. Cast it to an integer.
  2. Cast it back to a floating-point.
  3. Compare with original value.

Something like this:

double val = ... ;  //  Value

if ((double)(long long)val == val){
    //  Exactly representable
}

floor() and ceil() are also fair game (though they may fail if the value overflows an integer):

floor(val) == val
ceil(val) == val

And here's a messy bit-mask solution:
This uses union type-punning and assumes IEEE double-precision. Union type-punning is only valid in C99 TR2 and later.

int representable(double x){
    //  Handle corner cases:
    if (x == 0)
      return 1;

    //  -2^63 is representable as a signed 64-bit integer, but +2^63 is not.
    if (x == -9223372036854775808.)
      return 1;

    //  Warning: Union type-punning is only valid in C99 TR2 or later.
    union{
        double f;
        uint64_t i;
    } val;

    val.f = x;

    uint64_t exp = val.i & 0x7ff0000000000000ull;
    uint64_t man = val.i & 0x000fffffffffffffull;
    man |= 0x0010000000000000ull;  //  Implicit leading 1-bit.

    int shift = (exp >> 52) - 1075;
    //  Out of range
    if (shift < -52 || shift > 10)
        return 0;

    //  Test mantissa
    if (shift < 0){
        shift = -shift;
        return ((man >> shift) << shift) == man;
    }else{
        return ((man << shift) >> shift) == man;
    }
}
like image 120
Mysticial Avatar answered Sep 20 '22 20:09

Mysticial