使用按位运算将 Int 转换为 Float 或 Float 为 Int(软件浮点) [英] Converting Int to Float or Float to Int using Bitwise operations (software floating point)

查看:29
本文介绍了使用按位运算将 Int 转换为 Float 或 Float 为 Int(软件浮点)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道您是否可以帮助解释将整数转换为浮点数或将浮点数转换为整数的过程.对于我的班级,我们将仅使用按位运算符来执行此操作,但我认为对从类型到类型的转换的坚定理解将在此阶段对我有更多帮助.

据我目前所知,要使 int 浮点数,您必须将整数转换为二进制数,通过找到有效数、指数和分数来标准化整数的值,然后从那里输出浮点数中的值?

对于float转int,你需要将值分成有效数、指数和分数,然后逆向上面的指令得到一个int值?

<小时>

我试图按照这个问题的说明进行操作: )

因此,IEEE-754 编号 0x40000000 解释如下:

  • 位 31 = 0:正值
  • 位 30 .. 23 = 0x80:指数 = 128 - 127 = 1(又名 21)
  • 位 22 .. 0 都是 0:有效数 = 1.00000000_00000000_0000000.(注意我恢复了隐藏的 1).

所以值是 1.0 x 21 = 2.0.

要将上述有限范围内的 unsigned int 转换为 IEEE-754 格式的内容,您可以使用如下所示的函数.它需要以下步骤:

  • 将整数的前导 1 与浮点表示中 hidden 1 的位置对齐.
  • 在对齐整数时,记录进行的总移位次数.
  • 掩盖隐藏的 1.
  • 使用移位次数,计算指数并将其附加到数字上.
  • 使用reinterpret_cast,将生成的位模式转换为float.这部分是一个丑陋的黑客,因为它使用了类型双关指针.您也可以通过滥用 union 来做到这一点.某些平台提供了一种内在操作(例如 _itof)来使这种重新解释不那么难看.

有更快的方法可以做到这一点;如果不是超级有效的话,这个方法在教学上是有用的:

float uint_to_float(unsigned int significand){//只支持 0 <有效数<1<<24.if (significand == 0 || significand >= 1 << 24)返回-1.0;//或 abort();或者任何你想要的.int 移位 = 0;//将有效数的前导 1 与隐藏的 1 对齐// 位置.计算所需的轮班次数.而 ((有效数 & (1 << 23)) == 0){有效数<<= 1;班次++;}//数字 1.0 的指数为 0,并且需要为//左移 23 次.然而,数字 2.0 有一个//1 的指数,只需要向左移动 22 次.//因此,指数应该是 (23 - shifts).IEEE-754//格式需要 127 的偏差,所以指数字段//由以下表达式给出:unsigned int 指数 = 127 + 23 - 移位;//现在合并有效数和指数.一定要脱光//有效数中隐藏的 1.unsigned int 合并 = (指数<<23) |(有效数 & 0x7FFFFF);//重新解释为浮点数并返回.这是一个邪恶的黑客.返回 *reinterpret_cast<浮动* >( &merged );}

您可以使用检测数字中前导 1 的函数来提高此过程的效率.(这些名称有时类似于 clz 表示计数前导零",或 norm 表示规范化".)

您还可以通过记录符号、取整数的绝对值、执行上述步骤,然后将符号放入数字的第 31 位来将其扩展到有符号数.

对于整数 >= 224,整个整数不适合 32 位浮点格式的有效位字段.这就是您需要舍入"的原因:为了使值合适,您会丢失 LSB.因此,多个整数最终将映射到相同的浮点模式.精确映射取决于舍入模式(向 -Inf 舍入、向 +Inf 舍入、向零舍入、向最近的偶数舍入).但事实是,你不能在没有损失的情况下将 24 位塞入少于 24 位.

您可以通过上面的代码看到这一点.它的工作原理是将前导 1 与隐藏 1 位置对齐.如果值 >= 224,则代码需要移,而不是,这必然会将 LSB 移开.舍入模式只是告诉您如何处理移开的位.

I was wondering if you could help explain the process on converting an integer to float, or a float to an integer. For my class, we are to do this using only bitwise operators, but I think a firm understanding on the casting from type to type will help me more in this stage.

From what I know so far, for int to float, you will have to convert the integer into binary, normalize the value of the integer by finding the significand, exponent, and fraction, and then output the value in float from there?

As for float to int, you will have to separate the value into the significand, exponent, and fraction, and then reverse the instructions above to get an int value?


I tried to follow the instructions from this question: Casting float to int (bitwise) in C.
But I was not really able to understand it.

Also, could someone explain why rounding will be necessary for values greater than 23 bits when converting int to float?

解决方案

First, a paper you should consider reading, if you want to understand floating point foibles better: "What Every Computer Scientist Should Know About Floating Point Arithmetic," http://www.validlab.com/goldberg/paper.pdf

And now to some meat.

The following code is bare bones, and attempts to produce an IEEE-754 single precision float from an unsigned int in the range 0 < value < 224. That's the format you're most likely to encounter on modern hardware, and it's the format you seem to reference in your original question.

IEEE-754 single-precision floats are divided into three fields: A single sign bit, 8 bits of exponent, and 23 bits of significand (sometimes called a mantissa). IEEE-754 uses a hidden 1 significand, meaning that the significand is actually 24 bits total. The bits are packed left to right, with the sign bit in bit 31, exponent in bits 30 .. 23, and the significand in bits 22 .. 0. The following diagram from Wikipedia illustrates:

The exponent has a bias of 127, meaning that the actual exponent associated with the floating point number is 127 less than the value stored in the exponent field. An exponent of 0 therefore would be encoded as 127.

(Note: The full Wikipedia article may be interesting to you. Ref: http://en.wikipedia.org/wiki/Single_precision_floating-point_format )

Therefore, the IEEE-754 number 0x40000000 is interpreted as follows:

  • Bit 31 = 0: Positive value
  • Bits 30 .. 23 = 0x80: Exponent = 128 - 127 = 1 (aka. 21)
  • Bits 22 .. 0 are all 0: Significand = 1.00000000_00000000_0000000. (Note I restored the hidden 1).

So the value is 1.0 x 21 = 2.0.

To convert an unsigned int in the limited range given above, then, to something in IEEE-754 format, you might use a function like the one below. It takes the following steps:

  • Aligns the leading 1 of the integer to the position of the hidden 1 in the floating point representation.
  • While aligning the integer, records the total number of shifts made.
  • Masks away the hidden 1.
  • Using the number of shifts made, computes the exponent and appends it to the number.
  • Using reinterpret_cast, converts the resulting bit-pattern to a float. This part is an ugly hack, because it uses a type-punned pointer. You could also do this by abusing a union. Some platforms provide an intrinsic operation (such as _itof) to make this reinterpretation less ugly.

There are much faster ways to do this; this one is meant to be pedagogically useful, if not super efficient:

float uint_to_float(unsigned int significand)
{
    // Only support 0 < significand < 1 << 24.
    if (significand == 0 || significand >= 1 << 24)
        return -1.0;  // or abort(); or whatever you'd like here.

    int shifts = 0;

    //  Align the leading 1 of the significand to the hidden-1 
    //  position.  Count the number of shifts required.
    while ((significand & (1 << 23)) == 0)
    {
        significand <<= 1;
        shifts++;
    }

    //  The number 1.0 has an exponent of 0, and would need to be
    //  shifted left 23 times.  The number 2.0, however, has an
    //  exponent of 1 and needs to be shifted left only 22 times.
    //  Therefore, the exponent should be (23 - shifts).  IEEE-754
    //  format requires a bias of 127, though, so the exponent field
    //  is given by the following expression:
    unsigned int exponent = 127 + 23 - shifts;

    //  Now merge significand and exponent.  Be sure to strip away
    //  the hidden 1 in the significand.
    unsigned int merged = (exponent << 23) | (significand & 0x7FFFFF);


    //  Reinterpret as a float and return.  This is an evil hack.
    return *reinterpret_cast< float* >( &merged );
}

You can make this process more efficient using functions that detect the leading 1 in a number. (These sometimes go by names like clz for "count leading zeros", or norm for "normalize".)

You can also extend this to signed numbers by recording the sign, taking the absolute value of the integer, performing the steps above, and then putting the sign into bit 31 of the number.

For integers >= 224, the entire integer does not fit into the significand field of the 32-bit float format. This is why you need to "round": You lose LSBs in order to make the value fit. Thus, multiple integers will end up mapping to the same floating point pattern. The exact mapping depends on the rounding mode (round toward -Inf, round toward +Inf, round toward zero, round toward nearest even). But the fact of the matter is you can't shove 24 bits into fewer than 24 bits without some loss.

You can see this in terms of the code above. It works by aligning the leading 1 to the hidden 1 position. If a value was >= 224, the code would need to shift right, not left, and that necessarily shifts LSBs away. Rounding modes just tell you how to handle the bits shifted away.

这篇关于使用按位运算将 Int 转换为 Float 或 Float 为 Int(软件浮点)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆