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) {
...
}

float 操作や、あらゆる種類のキャストを行うことは許可されていません。

今、私はこのサイトで与えられた最初のアルゴリズムを実装しようとしました: 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: 私のコードから、このアルゴリズムの作業も開始したことがわかるかもしれません (このサイトを参照):

仮数部は 9 ビットしかないため、10 ビットの 2 の補数の整数を想定しましたが、プロセスはより多くのビットに一般化されます。

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ですが、最大のintが2147483647であるため、-2147483648の絶対値がないことです。これを回避することはできますが、その1ビットパターンに対して特別なケースを作成します(0の場合と同様)。

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

もう1つの問題は、0から32767までの数値に対してのみ機能するアルゴリズムをコピーしたことです。記事のさらに下の方で、すべてのintに拡張する方法を説明していますが、使用が許可されていない可能性のある操作を使用しています。

編集で言及したアルゴリズムに基づいて、最初から作成することをお勧めします。これは、0に向かって丸められるC#のバージョンです。

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, 0x7ffffffff] の 4 つのケースを区別できます。最初の 2 つは特殊なケースで、最後の 2 つのケースでは符号ビット (および入力の絶対値) を簡単に取得できます。署名なしにキャストする場合は、コメントで述べたように、0x80000000 を特殊なケースにしなくても問題ありません。

次に、指数を見つけます。簡単な (そしてコストのかかる) ループの方法と、これを行うためのよりトリッキーですがより高速な方法があります。これに関する私の絶対的なお気に入りのページは、Sean Anderson のbit hacks pageです。アルゴリズムの 1 つは、わずか 7 回の演算で整数の log 2を見つける非常に迅速なループのない方法を示しています。

指数がわかれば、仮数を見つけるのは簡単です。先頭の 1 ビットを削除し、指数の値に応じて結果を左または右にシフトします。

高速 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 に答える