ntrsBIG
ntrsBIG

Reputation: 39

Why does the function work with float properly but doesn't work with double?

I found the code that checks whether a float is a power of 2:

int isPowOf2(float number) {
    union {
        float   floatRepresent;
        int     intRepresent;
    } bitset;

    bitset.floatRepresent = number;

    if((bitset.intRepresent & ((1 << 23)-1)) != 0)
        return ((bitset.intRepresent & (bitset.intRepresent-1)) == 0); // denormalized number
    int power = bitset.intRepresent >> 23;
    return power > 0 && power < 255;
}

// ...

printf("[%f -> %d] ",2.0,isPowOf2(2.0f)); // [2.000000 -> 1] 
printf("[%f -> %d] ",4.0,isPowOf2(4.0f)); // [4.000000 -> 1]
printf("[%f -> %d] ",0.25,isPowOf2(0.25f)); // [0.250000 -> 1]
printf("[%f -> %d]\n ",11.0,isPowOf2(11.0f)); // [11.000000 -> 0]

It works without issues as you can see in comments. But when I try to turn this program into the version for double numbers, it gives wrong results:

int isPowOf2(double number) {
    union {
        double      floatRepresent;
        long long   intRepresent;
    } bitset;

    bitset.floatRepresent = number;

    if((bitset.intRepresent & ((1 << 53)-1)) != 0)
        return ((bitset.intRepresent & (bitset.intRepresent-1)) == 0); // denormalized number
    int power = bitset.intRepresent >> 53;
    return power > 0 && power < 2047;
}

// ...

printf("[%f -> %d] ",2.0,isPowOf2(2.0)); // [2.000000 -> 1] 
printf("[%f -> %d] ",4.0,isPowOf2(4.0)); // [4.000000 -> 0]
printf("[%f -> %d] ",0.25,isPowOf2(0.25)); // [0.250000 -> 0]
printf("[%f -> %d]\n ",11.0,isPowOf2(11.0)); // [11.000000 -> 0]

Could you please explain what's the problem?

Upvotes: 0

Views: 102

Answers (2)

chux
chux

Reputation: 153338

Code is performing an invalid shift. 1 is an int. A long long is needed. @Robᵩ

union {
  double floatRepresent;
  long long intRepresent;
} bitset;

// if((bitset.intRepresent & ((1 << 53)-1)) != 0)
if((bitset.intRepresent & ((1LL << 53)-1)) != 0)

Code is using the wrong constant. An IEEE 754 binary64 double has a 52 bit encoded significand. @njuffa

// if((bitset.intRepresent & ((1 << 53)-1)) != 0)
if((bitset.intRepresent & ((1LL << (53-1))-1)) != 0)

Code also does not properly work with +infinity.

// return power > 0 && power < 2047;
return power > 0 && power < 1023;  // Candidate fix for infinity.

Upvotes: 1

Weather Vane
Weather Vane

Reputation: 34575

The reason for failure is the wrong number of bits in the significand.

In the case of float 23 bits are stored of 24.

In the case of double 52 bits are stored of 53.

Having corrected that, and added the LL qualifier (as mentioned in comments) the offending line becomes

if((bitset.intRepresent & ((1LL << 52)-1)) != 0) {

and gives the same result as for float.

Upvotes: 2

Related Questions