简体   繁体   中英

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?

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 .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM