Reputation: 99
/*
* 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
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