我需要构建一个函数,它返回(float)x的位级等价物,而不使用任何浮动数据类型,操作或常量。我想我拥有它,但是当我运行测试文件时,它会返回一个无限循环。任何调试帮助将不胜感激。
我可以使用任何整数/无符号运算,包括||,&&,if,while。 另外,我只能使用30次操作
unsigned float_i2f(int x) {
printf("\n%i", x);
if (!x) {return x;}
int mask1 = (x >> 31);
int mask2 = (1 << 31);
int sign = x & mask2;
int complement = ~x + 1;
//int abs = (~mask1 & x) + (mask1 & complement);
int abs = x;
int i = 0, temp = 0;
while (!(temp & mask2)){
temp = (abs <<i);
i = i + 1;
}
int E = 32 - i;
int exp = 127 + E;
abs = abs & (-1 ^ (1 << E));
int frac;
if ((23 - E)>0)
frac = (abs << (23 - E));
else
frac = (abs >> (E - 23));
int rep = sign + (exp << 23) + frac;
return rep;
}
回应非常有用的评论和答案,这里是更新的代码,现在只有0x80000000失败:
unsigned float_i2f(int x) {
int sign;
int absX;
int E = -1;
int shift;
int exp;
int frac;
// zero is the same in int and float:
if (!x) {return x;}
// sign is bit 31: that bit should just be transferred to the float:
sign = x & 0x80000000;
// if number is < 0, take two's complement:
if (sign != 0) {
absX = ~x + 1;
}
else
absX = x;
shift = absX;
while ((!!shift) && (shift != -1)) {
//std::cout << std::bitset<32>(shift) << "\n";
E++;
shift = (shift >> 1);
}
if (E == 30) { E++;}
exp = E + 127+24;
exp = (exp << 23);
frac = (absX << (23 - E)) & 0x007FFFFF;
return sign + exp + frac;
}
任何人都知道修改后的代码中的错误在哪里?再次感谢大家!
答案 0 :(得分:2)
您可以做很多事情来改进代码并进行清理。首先,添加评论!其次,(并减少操作次数),您可以组合某些东西。第三, - 区分“可以完全表示的整数”和“不能表示的整数”。
以下是一些将这些内容付诸实践的示例代码;我实际上无法编译和测试这个,所以可能存在一些错误 - 我试图展示一种方法,而不是为你做任务......
unsigned float_i2f(int x) {
// convert integer to its bit-equivalent floating point representation
// but return it as an unsigned integer
// format:
// 1 sign bit
// 8 exponent bits
// 23 mantissa bits (plus the 'most significant bit' which is always 1
printf("\n%i", x);
// zero is the same in int and float:
if (x == 0) {return x;}
// sign is bit 31: that bit should just be transferred to the float:
sign = x & 0x8000;
// if number is < 0, take two's complement:
int absX;
if(sign != 0) {
absX = ~x + 1;
}
else
absX = x;
}
// Take at most 24 bits:
unsigned int bits23 = 0xFF800000;
unsigned int bits24 = 0xFF000000;
unsigned E = 127-24; // could be off by 1
// shift right if there are bits above bit 24:
while(absX & bits24) {
E++; // check that you add and don't subtract...
absX >>= 1;
}
// shift left if there are no bits above bit 23:
// check that it terminates at the right point.
while (!(absX & bits23))
E--; // check direction
absX <<= 1;
}
// now put the numbers we have together in the return value:
// check that they are truncated correctly
return sign | (E << 23) | (absX & ~bits23);
}
答案 1 :(得分:1)
尝试了适用于任何大小的解决方案。
不依赖于2的赞美
适用于INT_MIN。
从@Floris那里学到很多东西
[编辑]调整以进行舍入和其他改进
#include <stdio.h>
int Round(uint32_t Odd, unsigned RoundBit, unsigned StickyBit, uint32_t Result);
int Inexact;
// Select your signed integer type: works with any one
//typedef int8_t integer;
//typedef int16_t integer;
//typedef int32_t integer;
typedef int64_t integer;
//typedef intmax_t integer;
uint32_t int_to_IEEEfloat(integer x) {
uint32_t Result;
if (x < 0) { // Note 1
Result = 0x80000000;
} else {
Result = 0;
x = -x; // Use negative absolute value. Note 2
}
if (x) {
uint32_t Expo = 127 + 24 - 1;
static const int32_t m2Power23 = -0x00800000;
static const int32_t m2Power24 = -0x01000000;
unsigned RoundBit = 0;
unsigned StickyBit = 0;
while (x <= m2Power24) { // Note 3
StickyBit |= RoundBit;
RoundBit = x&1;
x /= 2;
Expo++;
}
// Round. Note 4
if (Round(x&1, RoundBit, StickyBit, Result) && (--x <= m2Power24)) {
x /= 2;
Expo++;
}
if (RoundBit | StickyBit) { // Note 5
Inexact = 1; // TBD: Set FP inexact flag
}
int32_t i32 = x; // Note 6
while (i32 > m2Power23) {
i32 *= 2;
Expo--;
}
if (Expo >= 0xFF) {
Result |= 0x7F800000; // Infinity Note 7
} else {
Result |= (Expo << 23) | ((-i32) & 0x007FFFFF);
}
}
return Result;
}
/*
Note 1 If `integer` was a signed-magnitude or 1s compliment, then +0 and -0 exist.
Rather than `x<0`, this should be a test if the sign bit is set.
The following `if (x)` will not be taken on +0 and -0.
This provides the corresponding float +0.0 and -0.0 be returned.
Note 2 Overflow will _not_ occur using 2s compliment, 1s compliment or sign magnitude.
We are insuring x at this point is < 0.
Note 3 Right shifting may shift out a 1. Use RoundBit and StickyBit to keep
track of bits shifted out for later rounding determination.
Note 4 Round as needed here. Possible to need to shift once more after rounding.
Note 5 If either RoundBit or StickyBit set, the floating point inexact flag may be set.
Note 6 Since the `Integer` type maybe be less than 32 bits, we need to convert
to a 32 bit integer as IEEE float is 32 bits.FILE
Note 7 Infinity only expected in Integer was 129 bits or larger.
*/
int Round(uint32_t Odd, unsigned RoundBit, unsigned StickyBit, uint32_t Result) {
// Round to nearest, ties to even
return (RoundBit) && (Odd || StickyBit);
// Truncate toward 0
// return 0;
// Truncate away from 0
// return RoundBit | StickyBit
// Truncate toward -Infinity
// return (RoundBit | StickyBit) || Result
}
// For testing
float int_to_IEEEfloatf(integer x) {
union {
float f;
uint32_t u;
} xx; // Overlay a float with a 32-bit unsigned integer
Inexact = 0;
printf("%20lld ", (long long) x);
xx.u = int_to_IEEEfloat(x);
printf("%08lX ", (long) xx.u);
printf("%d : ", Inexact);
printf("%.8e\n", xx.f);
return xx.f;
}
int main() {
int_to_IEEEfloatf(0x0);
int_to_IEEEfloatf(0x1);
int_to_IEEEfloatf(-0x1);
int_to_IEEEfloatf(127);
int_to_IEEEfloatf(-128);
int_to_IEEEfloatf(12345);
int_to_IEEEfloatf(32767);
int_to_IEEEfloatf(-32768);
int_to_IEEEfloatf(16777215);
int_to_IEEEfloatf(16777216);
int_to_IEEEfloatf(16777217);
int_to_IEEEfloatf(2147483647L);
int_to_IEEEfloatf(-2147483648L);
int_to_IEEEfloatf( 9223372036854775807LL);
int_to_IEEEfloatf(-9223372036854775808LL);
return 0;
}
答案 2 :(得分:0)
当说30 operations
时你是否计算了循环的迭代次数?
if (!x) {return x;}
只处理正0。为什么不掩盖标志,它既适用于零又
if (!(x & 0x7FFFFFFF)) {return x;}
此外,不需要许多指令,例如
complement = ~x + 1;
只需x = -x
就足够了,因为x以后不再使用了,absX或补充只是多余的。一个否定指令比2个操作快,对吧?
!!shift
也比shift != 0
慢。它仅在您需要将其用作仅0和1的表达式时才有用,否则它是多余的。
另一个问题是签名操作有时可能比无符号操作慢,所以如果没有必要,你不应该将变量声明为int
。例如,shift = (shift >> 1)
将执行算术移位(在大多数编译器实现中),这可能会导致意外结果。
为了找到第一个位集,可以使用instructions,不需要移位和测试。只需找到位位置并将值移位一次。如果您不允许使用内在函数,那么在Bit Twiddling Hacks上有很多快速方法可以做到这一点。