如何手动(按位)执行(浮点)x?

现在,这是我应该实现的函数的函数头:

/* * float_from_int - 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_from_int(int x) { ... } 

我们不允许进行浮动操作或任何类型的铸造。

现在我尝试实现在这个站点给出的第一个算法: http : //locklessinc.com/articles/i2f/

这是我的代码:

 unsigned float_from_int(int x) { // grab sign bit int xIsNegative = 0; int absValOfX = x; if(x < 0){ xIsNegative = 1; absValOfX = -x; } // zero case if(x == 0){ return 0; } if(x == 0x80000000){ //Updated to add this return 0xcf000000; } //int shiftsNeeded = 0; /*while(){ shiftsNeeded++; }*/ unsigned I2F_MAX_BITS = 15; unsigned I2F_MAX_INPUT = ((1 << I2F_MAX_BITS) - 1); unsigned I2F_SHIFT = (24 - I2F_MAX_BITS); unsigned result, i, exponent, fraction; if ((absValOfX & I2F_MAX_INPUT) == 0) result = 0; else { exponent = 126 + I2F_MAX_BITS; fraction = (absValOfX & I2F_MAX_INPUT) << I2F_SHIFT; i = 0; while(i < I2F_MAX_BITS) { if (fraction & 0x800000) break; else { fraction = fraction << 1; exponent = exponent - 1; } i++; } result = (xIsNegative << 31) | exponent << 23 | (fraction & 0x7fffff); } return result; } 

但它不起作用(见下面的测试错误):

 ERROR: Test float_from_int(8388608[0x800000]) failed... ...Gives 0[0x0]. Should be 1258291200[0x4b000000] 

我不知道从哪里开始。 我应该如何从这个int解析浮点数?

编辑#1:你可能从我的代码中看到我也开始研究这个算法( 参见这个网站 ):

我假设10位,2的补码,整数,因为尾数只有9位,但该过程推广到更多位。

 Save the sign bit of the input and take the absolute value of the input. Shift the input left until the high order bit is set and count the number of shifts required. This forms the floating mantissa. Form the floating exponent by subtracting the number of shifts from step 2 from the constant 137 or (0h89-(#of shifts)). Assemble the float from the sign, mantissa, and exponent. 

但是,这似乎并不正确。 我怎么能转换0x80000000? 没有意义。

编辑#2:我认为这是因为我说最大位是15 ……嗯……

编辑#3:搞砸那个旧算法,我重新开始:

 unsigned float_from_int(int x) { // grab sign bit int xIsNegative = 0; int absValOfX = x; if(x > counter) & 1) != 1 && shiftsNeeded < 32){ counter++; shiftsNeeded++; } unsigned exponent = shiftsNeeded + 127; unsigned result = (xIsNegative << 31) | (exponent << 23); return result; 

这是我得到的错误(我想我已经超过了最后一个错误):

 ERROR: Test float_from_int(-2139095040[0x80800000]) failed... ...Gives -889192448[0xcb000000]. Should be -822149120[0xceff0000] 

可能有助于了解:absValOfX = 7f800000(使用printf)

编辑#4:啊,我发现指数错了,需要从左边算起,然后减去32我相信。

编辑#5:我重新开始,现在试图处理奇怪的舍入问题……

  if (x == 0){ return 0; // 0 is a special case because it has no 1 bits } if (x >= 0x80000000 && x <= 0x80000040){ return 0xcf000000; } // Save the sign bit of the input and take the absolute value of the input. unsigned signBit = 0; unsigned absX = (unsigned)x; if (x < 0) { signBit = 0x80000000u; absX = (unsigned)-x; } // Shift the input left until the high order bit is set to form the mantissa. // Form the floating exponent by subtracting the number of shifts from 158. unsigned exponent = 158; while ((absX & 0x80000000) == 0) { exponent--; absX <> 7) & 1 & (absX >> 8); // compute mantissa unsigned mantissa = (absX >> 8) + ((negativeRoundUp) || (!signBit & (absX >> 7) & (exponent > 8 = %x, exponent = %i, mantissa = %x\n", absX, (absX >> 8), exponent, mantissa); // Assemble the float from the sign, mantissa, and exponent. return signBit | ((exponent << 23) + (signBit & negativeRoundUp)) | ( (mantissa) & 0x7fffff); 

 absX = fe000084, absX >> 8 = fe0000, exponent = 156, mantissa = fe0000 ERROR: Test float_from_int(1065353249[0x3f800021]) failed... ...Gives 1316880384[0x4e7e0000]. Should be 1316880385[0x4e7e0001] 

编辑#6

再做一遍,仍然,舍入不能正常工作。 我试图破解一些四舍五入,但它不会起作用……

 unsigned float_from_int(int x) { /* If N is negative, negate it in two's complement. Set the high bit (2^31) of the result. If N < 2^23, left shift it (multiply by 2) until it is greater or equal to. If N ≥ 2^24, right shift it (unsigned divide by 2) until it is less. Bitwise AND with ~2^23 (one's complement). If it was less, subtract the number of left shifts from 150 (127+23). If it was more, add the number of right shifts to 150. This new number is the exponent. Left shift it by 23 and add it to the number from step 3. */ printf("---------------\n"); //printf("x = %i (%x), -x = %i, (%x)\n", x, x, -x, -x); if(x == 0){ return 0; } if(x == 0x80000000){ return 0xcf000000; } // If N is negative, negate it in two's complement. Set the high bit of the result unsigned signBit = 0; if (x < 0){ signBit = 0x80000000; x = -x; } printf("abs val of x = %i (%x)\n", x, x); int roundTowardsZero = 0; int lastDigitLeaving = 0; int shiftAmount = 0; int originalAbsX = x; // If N < 2^23, left shift it (multiply it by 2) until it is great or equal to. if(x < (8388608)){ while(x < (8388608)){ //printf(" minus shift and x = %i", x ); x = x <= 2^24, right shfit it (unsigned divide by 2) until it is less. else if(x >= (16777215)){ while(x >= (16777215)){ /*if(x & 1){ roundTowardsZero = 1; printf("zzz Got here ---"); }*/ lastDigitLeaving = (x >> 1) & 1; //printf(" plus shift and x = %i", x); x = x >> 1; shiftAmount++; } //Round towards zero x = (x + (lastDigitLeaving && (!(originalAbsX > 16777216) || signBit))); printf("x = %i\n", x); //shiftAmount = shiftAmount + roundTowardsZero; } printf("roundTowardsZero = %i, shiftAmount = %i (%x)\n", roundTowardsZero, shiftAmount, shiftAmount); // Bitwise AND with 0x7fffff x = x & 0x7fffff; unsigned exponent = 150 + shiftAmount; unsigned rightPlaceExponent = exponent << 23; printf("exponent = %i, rightPlaceExponent = %x\n", exponent, rightPlaceExponent); unsigned result = signBit | rightPlaceExponent | x; return result; 

问题是最低的int是-2147483648,但最高的是2147483647,因此没有绝对值-2147483648。 虽然你可以解决它,但我会为这一位模式做一个特殊情况(就像你为0做的那样):

 if (x == 0) return 0; if (x == -2147483648) return 0xcf000000; 

另一个问题是你复制了一个只适用于从0到32767的数字的算法。在文章的下面,他们解释了如何将它扩展到所有的int,但是它使用了你可能不允许使用的操作。

我建议根据编辑中提到的算法从头开始编写。 这是C#中的一个向0舍入的版本:

 uint float_from_int(int x) { if (x == 0) return 0; // 0 is a special case because it has no 1 bits // Save the sign bit of the input and take the absolute value of the input. uint signBit = 0; uint absX = (uint)x; if (x < 0) { signBit = 0x80000000u; absX = (uint)-x; } // Shift the input left until the high order bit is set to form the mantissa. // Form the floating exponent by subtracting the number of shifts from 158. uint exponent = 158; while ((absX & 0x80000000) == 0) { exponent--; absX <<= 1; } // compute mantissa uint mantissa = absX >> 8; // Assemble the float from the sign, mantissa, and exponent. return signBit | (exponent << 23) | (mantissa & 0x7fffff); } 

算法的基本公式是确定符号,指数和尾数位,然后将结果打包成一个整数。 以这种方式分解可以很容易地清楚地分离代码中的任务,并使解决问题(和测试算法)变得更加容易。

符号位是最简单的,摆脱它使得找到指数更容易。 您可以区分四种情况:0,0×80000000,[ – 0x7ffffff,-1]和[1,0x7fffffff]。 前两个是特殊情况,您可以在最后两种情况下(以及输入的绝对值)轻松获取符号位。 如果您要转换为无符号,您可以使用我在评论中提到的特殊shell0x80000000。

接下来,找到指数 – 这是一种简单(且成本高昂)的循环方式,以及更简单但更快速的方法。 我最喜欢的页面是Sean Anderson的黑客页面 。 其中一种算法显示了一种非常快速的无循环方式,仅在七次操作中查找整数的log 2

一旦你知道指数,那么找到尾数很容易。 您只需删除前一位,然后根据指数的值向左或向右移动结果。

如果使用快速log 2算法,最终可能会得到一个使用不超过20次操作的算法。

处理0x80000000非常简单:

 int xIsNegative = 0; unsigned int absValOfX = x; if (x < 0) { xIsNegative = 1; absValOfX = -(unsigned int)x; } 

它摆脱了特殊的shell-2147483648因为该值可以表示为无符号值,而absValOfX应该始终为正。