【问题标题】:Extracting bits and reconstructing in C在 C 中提取位和重构
【发布时间】:2016-03-03 07:41:52
【问题描述】:

对于一个班级项目,我试图从 IEEE 浮点数中提取 3 位字段,将其乘以 0.5,然后重新构建该数字。我主要通过将它们推入无符号整数来进行提取:

x = 5.5
x: 1000000101100000000000000000000 (1085276160, 0x40b00000)
extracted sign: 0 (0, 0x0)
extracted exp: 10000001 (129, 0x81)
extracted sig: 01100000000000000000000 (1610612736, 0x60000000)

我的两个问题是:

a) 我如何将这些位放回原来的输入?我试过了:

return sign ^ exp ^ sig

并没有得到正确的结果。

b) 如何将数字乘以 0.5?乘以 sig 还是 exp?

【问题讨论】:

  • 您知道^ 运算符是exclusive bitwise or (xor)
  • 真的不知道这样行不行,但你可以尝试移位和添加,然后转换为浮点数
  • 查找并研究 IEEE 表示。然后做你的工作。我假设您不允许让 C 进行转换/转换。你会想乘以尾数,而不是符号或指数。
  • 我相信this 应该可以帮到你。
  • x=5.5 所以x*0.5=2.75。在2.75 上运行你的提取代码,你就会知道你应该生成什么位模式。

标签: c binary floating-point ieee-754


【解决方案1】:

a)您可以“但这些位返回”,并结合使用 ands(屏蔽)和 ors(将它们粘在一起)进行移位。使用程序员的计算器应该足以了解它的工作原理。

例如(伪C):

unsigned int sign = ...;
unsigned int exp  = ...;
unsigned int sig  = ...;

见:float format

unsigned int out;
out = ((sign & 1)<<31)|((exp & 0xff)<<23)|(sig & 0x7fffff);

&1、&0xff 和 &0x07fffff 的意义在于它们分别设置了 1、8 和 23 位。

b) 两者都可以,但是如果将指数移动 +-1,则与乘以/除以 2 相同。这不会降低“精度”,因为有效数字保持不变。如果指数已经处于最小值,那么唯一的方法就是开始减少有效值,这将是非规范化。

注意:没有存储的有效数字有一个隐含的高位 1 位。换句话说,除非指数为零,否则有第 24 位未存储并假定为 1。

【讨论】:

    【解决方案2】:

    a) 我如何将这些位放回原来的输入?

    要重新形成 float 的 32 位表示,请使用 |,而不是 ^

    #include <stdint.h>
    
    #define FLT_SIGN_SFT 31
    #define FLT_EXP_SFT  23
    #define FLT_SIG_SFT  0
    #define FLT_SIGN_MSK 1
    #define FLT_EXP_MSK  0xFF
    #define FLT_SIG_MSK  0x7FFFFF
    #define FLT_SIG_IMPLIED_BIT  (0x7FFFFF + 1)
    
    float float_form(uint32_t sign, uint32_t exp, uint32_t sig) {
      union {
        uint32_t u32;
        float f;
      } u;
      u.u32 = sign << FLT_SIGN_SFT | exp << FLT_EXP_SFT | sig << FLT_SIG_SFT;
      return u.f;
    }
    

    字节序问题:只要floatuint32_t 具有相同的字节序,就可以。需要进行其他调整。

    如何将数字乘以 0.5?乘以 sig 还是 exp?

    这很棘手。如果 x 是 NaN 或 Infinty,则什么也不做。如果x 是正常数,则减少指数(特殊情况:如果值现在是次正常的)。如果x 是次范数,则移动有效数字。如果移出 1 位,请考虑舍入。如果四舍五入导致指数发生变化,请进行调整。

    float float_div2(float f) {
      union {
        uint32_t u32;
        float f;
      } u;
      u.f = f;
      uint32_t sign = (u.u32 >> FLT_SIGN_SFT) & FLT_SIGN_MSK;
      uint32_t exp = (u.u32 >> FLT_EXP_SFT) & FLT_EXP_MSK;
      uint32_t sig = (u.u32 >> FLT_SIG_SFT) & FLT_SIG_MSK;
      if (exp < FLT_EXP_MSK) {
        unsigned shift_out = 0;
        if (exp > 0) {
          exp--;
          if (exp == 0) {
            sig += FLT_SIG_IMPLIED_BIT;
            shift_out = sig % 2u;
            sig /= 2;
          }
        } else {
          shift_out = sig % 2u;
          sig /= 2;
        }
        if (shift_out > 0) {
          assert(exp == 0);
          // Assume round to even
          if (sig % 2) {
            sig++;
            if (sig >= FLT_SIG_IMPLIED_BIT) {
              sig -= FLT_SIG_IMPLIED_BIT;
              exp++;
            }
          } // end if (sig % 2)
        } // end if (exp > 0)
      } // end if (exp < FLT_EXP_MSK)
      return float_form(sign, exp, sig);
    }
    

    测试代码。已针对所有 float 成功测试。

    void float_div2_test(float x) {
      float y = x / 2.0f;
      float z = float_div2(x);
      if (memcmp(&y, &z, sizeof z)) {
        printf("%.10e %.10e %.10e\n", x, y, z);
        printf("%a %a %a\n", x, y, z);
        exit(1);
      }
    }
    
    void float_div2_tests() {
      union {
        uint32_t u32;
        float f;
      } u;
      u.u32 = 0;
      do {
        u.u32--;
        float_div2_test(u.f);
      } while (u.u32);
      puts("Success!");
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-14
      • 1970-01-01
      • 1970-01-01
      • 2011-08-07
      • 2012-02-10
      • 2014-12-28
      相关资源
      最近更新 更多