简体   繁体   English

提取位并在C中重构

[英]Extracting bits and reconstructing in C

For a class project I'm trying to extract 3 bit fields from an IEEE floating point number, multiply it by 0.5, and reconstruct the number. 对于一个班级项目,我试图从IEEE浮点数中提取3位字段,将其乘以0.5,然后重构该数。 I've gotten extraction mostly working by pushing them into unsigned integers: 我已经通过将它们推入无符号整数来进行提取:

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

My two questions are: 我的两个问题是:

a) How do I put these bits back to form the original input? a)如何将这些位放回原来的输入中? I tried: 我试过了:

return sign ^ exp ^ sig

and did not get the correct result. 却没有得到正确的结果。

b) How do I multiply the number by 0.5? b)如何将数字乘以0.5? Do I multiply sig or exp? 我要乘sig或exp吗?

a) you can "but these bits back" together with a combination of shifting with ands (to mask) and ors (to stick them back together). a)您可以将这些位“退回”,同时结合使用ands(以掩盖)和ors(以将它们重新粘在一起)进行移位。 Playing with a programmers' calculator should suffice to understand how it works. 玩程序员的计算器应该足以了解它是如何工作的。

For example (pseudo-C): 例如(pseudo-C):

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

See: float format 另请: 浮点格式

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

Where the significance of the & 1, & 0xff and & 0x07fffff is that they each have respectively 1, 8 and 23 bits set. &1,&0xff和&0x07fffff的意义在于它们分别设置了1、8和23位。

b) either would work, but if you move the exponent by +-1 it is the same as multiplying/dividing by two. b)都可以,但是如果将指数移动+ -1,则等于乘以/除以2。 This would not reduce the "precision" because the significand stays the same. 因为有效位数保持不变,所以这不会降低“精度”。 If the exponent is already at the minimum the only way to do it would then be to start reducing the signifcand, which would be denormalization. 如果指数已经最小,那么唯一的方法就是开始减小信号,这将是非规范化。

Note: there is an implicit high order 1 bit to the significand that is not stored. 注意:有效位数有一个隐含的高位1位,未存储。 In other words, unless the exponent is zero, there is a 24th one bit that is not stored and is assumed to be one. 换句话说,除非指数为零,否则不存储第24个1位,并假定为1。

a) How do I put these bits back to form the original input? a)如何将这些位放回原来的输入中?

To re-form the 32-bit representation of the float , use | 要重新形成float的32位表示形式,请使用| , not ^ . ,而不是^

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

Endian concerns: As long as both the float and uint32_t are of the same endian, this works. 字节序关注点:只要floatuint32_t都使用相同的字节序,则此方法有效。 Else adjustments are needed. 需要其他调整。

How do I multiply the number by 0.5? 如何将数字乘以0.5? Do I multiply sig or exp? 我要乘sig或exp吗?

This is tricky. 这很棘手。 If x is a NaN or Infinty, do nothing. 如果x是NaN或Infinty,则不执行任何操作。 If x in a normal number, decrement the exponent (special case: if value is now a sub-normal). 如果x为正常数,则递减指数(特殊情况:如果value现在为次正态)。 If x is a sub-normal, shift the significand. 如果x是次法线,则将有效数移位。 If a 1 bit was shifted out, consider rounding. 如果将一位移出,请考虑取整。 If rounding caused change in exponent, adjust. 如果四舍五入导致指数变化,请进行调整。

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

Test code. 测试代码。 Tested successfully against all float . 通过所有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!");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM