Jinwoo Park
Jinwoo Park

Reputation: 99

Why are these two codes giving a different output? // bits.c float_i2f

/* 
 * float_i2f - Return bit-level equivalent of expression (float) x
 *   Result is returned as unsigned int, but
 *   it is to be interpreted as the bit-level representation of a
 *   single-precision floating point values.
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 30
 *   Rating: 4
 */
unsigned float_i2f(int x) {
int sign = x & (1 << 31);
int minus = 1 << 31; 
int exp = 31;
int bias = 127;
int frac;
if (!x)
   return 0;
if (x == minus)
   return minus | ((exp + bias) << 23);
if (sign)
   x = ~x + 1;
while (!(x & minus))
{
   x <<= 1;
   exp -= 1;
}
frac = (((~minus) & x) >> 8);
if (x & 0x80 && ((frac & 1) ||((x & 0x7f) > 0)))
   frac++;
return sign + ((exp + bias) << 23) + frac;
}

I checked this code on driver.pl, which checks the correctness of answer. But I found something strange.

If the last return statement becomes

return sign | ((exp + bias) << 23) | frac;

it says that this does not work, but if I change this to

return sign + ((exp + bias) << 23) + frac;

it becomes correct.

What's the difference between two codes? Why the first one doesn't work?? Please help me :(

Upvotes: 1

Views: 2137

Answers (1)

Bob__
Bob__

Reputation: 12779

The problem rises with large numbers. Consider for example 225 - 1 = 33554431 and its 2's complement binary representation is

00000001 11111111 11111111 11111111           // or --> 0 00000011 11111111111111111111111

After having found the exponent, the code evaluates frac as

frac = (((~minus) & x) >> 8);                 //   -->  0 00000000 11111111111111111111111

But it has to round this, so it performs

if (x & 0x80 && ((frac & 1) ||((x & 0x7f) > 0)))
    frac++;                                     // -->  0 00000001 00000000000000000000000

Basically we have a carry and we should increase the exponent, like we would do if we had to represent 999 as 1x103 instead of 9.99x102. Now you can see the difference between | or +:

sign + ((exp + bias) << 23) + frac;              // --> 0 10011000 00000000000000000000000

sign | ((exp + bias) << 23) | frac;              // --> 0 10010111 00000000000000000000000

Upvotes: 1

Related Questions