8

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

/*
 * 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 < 0){
    xIsNegative = 1;
    absValOfX = -x;
  }


  // zero case
  if(x == 0){
    return 0;
  }
  if (x == 0x80000000){
    return 0xcf000000;
  }

  int shiftsNeeded = 0;

  int counter = 0;
  while(((absValOfX >> 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 <<= 1;
    }

  unsigned negativeRoundUp = (absX >> 7) & 1 & (absX >> 8);

  // compute mantissa
  unsigned mantissa = (absX >> 8) + ((negativeRoundUp) || (!signBit & (absX >> 7) & (exponent < 156)));
  printf("absX = %x, absX >> 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 << 1;
      shiftAmount--;
    }
  } // If N >= 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;
4

3 回答 3

3

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

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

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

我建议根据您编辑中提到的算法从头开始编写它。这是 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);
}
于 2012-09-09T03:55:39.260 回答
1

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

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

接下来,找到指数 - 有一种简单(且昂贵)的循环方式,以及一种更复杂但更快的方法来做到这一点。我最喜欢的页面是 Sean Anderson 的bit hacks 页面。其中一种算法显示了一种非常快速的无循环方法,只需七次操作即可找到整数的 log 2

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

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

于 2012-09-09T05:45:07.700 回答
0

处理0x80000000很容易:

int xIsNegative = 0;  
unsigned int absValOfX = x;  

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

它摆脱了特殊的大小写-2147483648,因为该值可以表示为无符号值,并且absValOfX应该始终为正数。

于 2012-09-09T05:07:15.780 回答