【问题标题】:How to generate an IEEE 754 Single-precision float using only integer arithmetic?如何仅使用整数运算生成 IEEE 754 单精度浮点数?
【发布时间】:2015-06-12 04:55:59
【问题描述】:

假设一个低端微处理器没有浮点运算,我需要生成一个 IEE754 单精度浮点格式数字来推送到一个文件中。

我需要编写一个函数,它接受三个整数作为符号、整数和小数,并返回一个字节数组,其中 4 个字节是 IEEE 754 单精度表示。

类似:

// Convert 75.65 to 4 byte IEEE 754 single precision representation
char* float = convert(0, 75, 65);

请问有人有任何指针或示例 C 代码吗?我特别难以理解如何转换尾数。

【问题讨论】:

  • 几乎所有嵌入式编译器都内置了对 IEEE 浮点算法的软件仿真的支持。即使是微型 8 位 MCU。您的目标是什么,您是否尝试过诱使编译器为您执行此操作?但是,您确实经常需要在编译器设置中显式启用它。
  • @doynax:此类库的问题是它们往往会使生成的固件膨胀。对于小型 MCU,这可以轻松地将二进制文件加倍。
  • 函数定义有问题:函数应该取sign, whole and hundredths。说“分数”并且只提供 65 给出分数的分子,但没有分母 - 当然,除非分母是隐含的 100。否则你如何编码 75.01
  • char* float 是什么意思?
  • 你不能为变量使用内置名称(你也不应该使用标准库名称)。

标签: c ieee-754


【解决方案1】:

您需要生成符号(1 位)、指数(8 位,2 的偏置幂)和小数/尾数(23 位)。

请记住,分数有一个隐含的前导“1”位,这意味着最高有效的前导“1”位 (2^22) 不以 IEEE 格式存储。例如,给定 0x755555(24 位)的一小部分,实际存储的位将是 0x355555(23 位)。

还请记住,小数会被移动,以便二进制点立即位于隐式前导“1”位的右侧。所以 IEEE 23 位小数 11 0101 0101... 表示 24 位二进制小数 1.11 0101 0101... 这意味着必须相应地调整指数。

【讨论】:

    【解决方案2】:

    值必须写成大端还是小端?逆位排序?

    如果你有空,你应该考虑将值写成字符串字面量。这样你就可以很容易地转换整数:只需写 int 部分并将“e0”写为指数(或省略指数并写“.0”)。

    对于二进制表示,您应该查看Wikipedia。最好的方法是首先将位域组装到uint32_t - 链接文章中给出了结构。请注意,如果整数的值超过 23 位,您可能需要四舍五入。记得对生成的值进行归一化。

    第二步是将uint32_t序列化为uint8_t数组。注意结果的字节序!

    如果你真的想要 8 位值,还要注意使用 uint8_t 作为结果;您应该使用无符号类型。对于中间表示,建议使用uint32_t,因为这样可以保证您对 32 位值进行操作。

    【讨论】:

    • 转换为字符串的成本比位操作高一到两个数量级。
    • 取决于您如何实现除法(对于 const 除以 10 有高度优化的版本)。优点是您独立于二进制格式。但是,我不建议它 - 请注意“想想”。这就是我为二进制转换添加的基础知识。
    【解决方案3】:

    你还没有去,所以没有赠品。

    请记住,您可以将两个 32 位整数 a 和 b 解释为十进制 a.b 作为指数为 2^-32(其中 ^ 是指数)的单个 64 位整数。

    因此,无需执行任何操作,您就可以在表单中得到它:

    s * m * 2^e
    

    唯一的问题是你的尾数太长而且你的数字没有标准化。

    使用可能的舍入步骤进行一些移位和加/减,就完成了。

    【讨论】:

    • @Olaf 因为它是 64 位的。显然,设置位的范围可能意味着它可以移动到位。但在我描述的原始形式中,在规范化的 IEEE 75 单精度浮点数中,有一个 64 位尾数指向 24 位。
    • 抱歉,我并不清楚我的意思。为什么你认为它对 OP 来说太长了?由于那是一个小型 MCU,因此 int 很可能只有 16 位(如果 int16_t 则为 15)。显然不超过 128 位的一小部分(他似乎以 1/100 的形式提供),它非常适合 f32 尾数。但显然他对他的问题失去了兴趣,所以让我们把它留在这儿吧。
    【解决方案4】:

    您可以使用软件浮点编译器/库。
    https://gcc.gnu.org/onlinedocs/gccint/Soft-float-library-routines.html

    【讨论】:

      【解决方案5】:

      基本前提是:

      1. 给定binary32float
      2. 形成组合whole 和派系部分hundredths 的二进制定点表示。此代码使用分别编码整个字段和百分之一字段的结构。重要的是,whole 字段至少为 32 位。
      3. 左移/右移(*2 和 /2)直到 MSbit 位于隐含的位位置,同时计数移位。一个稳健的解决方案还会注意到移出的非零位。
      4. 形成一个有偏的指数。
      5. 圆尾数和删除隐含位。
      6. 表格标志(此处未完成)。
      7. 结合以上 3 个步骤来形成答案。
      8. whole, hundredths 输入不会产生亚法线、无穷大和非数字,因此此处不讨论生成那些 float 的特殊情况。

      .

      #include <assert.h>
      #include <stdint.h>
      #define IMPLIED_BIT 0x00800000L
      
      typedef struct {
        int_least32_t whole;
        int hundreth;
      } x_xx;
      
      int_least32_t covert(int whole, int hundreth) {
        assert(whole >= 0 && hundreth >= 0 && hundreth < 100);
        if (whole == 0 && hundreth == 0) return 0;
        x_xx x = { whole, hundreth };
        int_least32_t expo = 0;
        int sticky_bit = 0; // Note any 1 bits shifted out
        while (x.whole >= IMPLIED_BIT * 2) {
          expo++;
          sticky_bit |= x.hundreth % 2;
          x.hundreth /= 2;
          x.hundreth += (x.whole % 2)*(100/2);
          x.whole /= 2;
        }
        while (x.whole < IMPLIED_BIT) {
          expo--;
          x.hundreth *= 2;
          x.whole *= 2;
          x.whole += x.hundreth / 100;
          x.hundreth %= 100;
        }
        int32_t mantissa = x.whole;
        // Round to nearest - ties to even
        if (x.hundreth >= 100/2 && (x.hundreth > 100/2 || x.whole%2 || sticky_bit)) {
          mantissa++;
        }
        if (mantissa >= (IMPLIED_BIT * 2)) {
          mantissa /= 2;
          expo++;
        }
        mantissa &= ~IMPLIED_BIT;  // Toss MSbit as it is implied in final
        expo += 24 + 126; // Bias: 24 bits + binary32 bias
        expo <<= 23; // Offset
        return expo | mantissa;
      }
      
      void test_covert(int whole, int hundreths) {
        union {
          uint32_t u32;
          float f;
        } u;
        u.u32 = covert(whole, hundreths);
        volatile float best = whole + hundreths / 100.0;
        printf("%10d.%02d --> %15.6e %15.6e Same:%d\n", whole, hundreths, u.f, best,
            best == u.f);
      }
      
      #include <limits.h>
      int main(void) {
        test_covert(75, 65);
        test_covert(0, 1);
        test_covert(INT_MAX, 99);
        return 0;
      
      }
      

      输出

              75.65 -->    7.565000e+01    7.565000e+01 Same:1
               0.01 -->    1.000000e-02    1.000000e-02 Same:1
      2147483647.99 -->    2.147484e+09    2.147484e+09 Same:1
      

      已知问题:未应用标志。

      【讨论】:

        猜你喜欢
        • 2021-10-06
        • 2013-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多