assembly - 使用按位运算将 Int 转换为 Float 或 Float 为 Int(软件浮点)

标签 assembly floating-point arm bit-manipulation ieee-754

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

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

至于 float 到 int,您必须将值分成有效数、指数和分数,然后反转上面的指令才能得到一个 int 值?

我试图按照这个问题的说明进行操作:Casting float to int (bitwise) in C .
但我真的无法理解它。

另外,有人可以解释为什么在将 int 转换为 float 时,大于 23 位的值需要舍入吗?

最佳答案

首先,如果你想更好地理解浮点数的弱点,你应该考虑阅读一篇论文:“每个计算机科学家应该知道的关于浮点运算的知识”,http://www.validlab.com/goldberg/paper.pdf

现在来点肉。

以下代码是基本代码,并尝试从 unsigned int 生成 IEEE-754 单精度浮点数在 0 < value < 224 范围内。这是您在现代硬件上最有可能遇到的格式,也是您在原始问题中似乎引用的格式。

IEEE-754 单精度浮点数分为三个字段:单个符号位、8 位指数和 23 位有效数(有时称为尾数)。 IEEE-754 使用隐藏的 1 有效位,这意味着有效位实际上总共是 24 位。这些位从左到右打包,符号位在第 31 位,指数在位 30 .. 23,有效数在位 22 .. 0。来自维基百科的下图说明:

floating point format

指数的偏差为 127,这意味着与浮点数关联的实际指数比存储在指数字段中的值小 127。因此,指数 0 将被编码为 127。

(注意:完整的维基百科文章可能对您感兴趣。引用:http://en.wikipedia.org/wiki/Single_precision_floating-point_format)

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

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

    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 );
    }
    

    您可以使用检测数字中前导 1 的函数来提高此过程的效率。 (这些有时会以 clz 表示“计数前导零”或 norm 表示“标准化”等名称。)

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

    对于大于等于 224 的整数,整个整数不适合 32 位浮点格式的有效位字段。这就是您需要“舍入”的原因:为了使值合适,您会丢失 LSB。因此,多个整数最终将映射到相同的浮点模式。精确映射取决于舍入模式(向 -Inf 舍入、向 +Inf 舍入、向零舍入、向最近的偶数舍入)。但事实是,您不能将 24 位塞入少于 24 位的数据而不会有一些损失。

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

    关于assembly - 使用按位运算将 Int 转换为 Float 或 Float 为 Int(软件浮点),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20302904/

    相关文章:

    ARM嵌入式延迟硬件计时器与CPU周期计数器

    将 C 代码转换为 MIPS(数组)

    assembly - 当变量超过 32 个时寄存器会发生什么情况?

    python-3.x - Python 中的舍入

    math - CLISP 中自然对数的精度不正确。可能出了什么问题?

    android - 使用 NEON 内在函数除以 float

    c - 为什么 GCC 使用 Mov 而不是 Push In 函数调用?

    c++ - 分配函数值和不分配给变量之间的速度差异

    c++ - ARM64 上浮点精度的奇怪问题

    linux - Android ARM 架构上的“noarch 二进制文件”