C 浮点表示法(使用位运算符)

C 浮点表示法(使用位运算符),c,floating-point,precision,C,Floating Point,Precision,这是打印浮点表示形式(即:x=(−1) ^标志·(1.m 22 m 21 m 20…m 0)·2^(e)−我还不明白其中的一些东西: 1) 工会的使用,为什么? 2) 尾数面具和指数面具,它们是做什么用的? 3) 在此处使用(&in): uint32_t exponent = ( t.bits >> MANTISSA_WIDTH ) & EXPONENT_MASK; uint32_t mantissa = ( t.bits & MANTISSA_MASK

这是打印浮点表示形式(即:x=(−1) ^标志·(1.m 22 m 21 m 20…m 0)·2^(e)−我还不明白其中的一些东西: 1) 工会的使用,为什么? 2) 尾数面具和指数面具,它们是做什么用的? 3) 在此处使用(&in):

  uint32_t exponent = ( t.bits >> MANTISSA_WIDTH ) & EXPONENT_MASK;
  uint32_t mantissa = ( t.bits  &  MANTISSA_MASK );
代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>

#define ABSOLUTE_WIDTH 31
#define MANTISSA_WIDTH 23
#define EXPONENT_WIDTH 8
#define EXPONENT_MASK 0xffu
#define MANTISSA_MASK 0x007fffffu
#define EXPONENT_BIAS 127

union float_bits {
  float f;
  uint32_t bits;
};

void print_float( FILE *output, float f ) {
  union float_bits t; t.f = f;

  uint32_t sign_bit = ( t.bits >> ABSOLUTE_WIDTH );
  uint32_t exponent = ( t.bits >> MANTISSA_WIDTH ) & EXPONENT_MASK;
  uint32_t mantissa = ( t.bits  &  MANTISSA_MASK );

  if( sign_bit != 0 ) {
    fprintf( output, "-" );
  }

  if( exponent > 2 * EXPONENT_BIAS ) {
    fprintf( output, "Inf\n" ); /* Infinity */
    return;
  } else if( exponent == 0 ) {
    fprintf( output, "0." ); /* Zero or Denormal */
    exponent = ( mantissa != 0 ) ? exponent + 1 : exponent;
  } else {
    fprintf( output, "1." ); /* Usual */
  }

  for( int k = MANTISSA_WIDTH - 1; k >= 0; --k ) {
    fprintf( output, "%d", ( mantissa >> k ) & 1 );
  }

  if( exponent != 0 || mantissa != 0 ) {
    fprintf( output, " * 2^%d\n", (int) ( exponent - EXPONENT_BIAS ) );
  }
}

int main() {
  FILE *input  = fopen( "floating.in",  "r" ),
       *output = fopen( "floating.out", "w" );

  size_t N; float f;
  fscanf( input, "%zu", &N );

  for( size_t i = 0; i < N; ++i ) {
    fscanf( input, "%f", &f );
    print_float( output, f );
  }

  fclose( input );
  fclose( output );
  return 0;
}
#包括
#包括
#包括
#包括
#定义绝对宽度31
#定义尾数宽度23
#定义指数_宽度8
#定义指数屏蔽0xffu
#定义尾数掩码0x007fffffu
#定义指数偏差127
联合浮点数{
浮动f;
uint32_t位;
};
无效打印浮点数(文件*输出,浮点数f){
联合浮点数t;t.f=f;
uint32符号位=(t位>>绝对宽度);
uint32指数=(t.bits>>尾数宽度)&指数掩码;
uint32_t尾数=(t位和尾数掩码);
如果(符号位!=0){
fprintf(输出“-”);
}
如果(指数>2*指数偏差){
fprintf(输出,“Inf\n”);/*无穷大*/
返回;
}否则(指数==0){
fprintf(输出,“0”);/*零或非规范*/
指数=(尾数!=0)?指数+1:指数;
}否则{
fprintf(输出,“1”);/*通常*/
}
对于(int k=尾数宽度-1;k>=0;--k){
fprintf(输出,“%d”,(尾数>>k)和1);
}
如果(指数!=0 | |尾数!=0){
fprintf(输出,“*2^%d\n”,(int)(指数-指数偏差);
}
}
int main(){
FILE*input=fopen(“floating.in”,“r”),
*输出=fopen(“浮点输出”,“w”);
尺寸N;浮球f;
fscanf(输入,“%zu”,&N);
对于(尺寸i=0;i
1) 工会的使用,为什么

位运算符仅适用于整数类型。由于明显的原因,无法将浮点数转换为整数。但一个并集可以定位重叠组件的内存。因此,通过写入浮点组件,然后读取整数组件,将返回浮点数的整数表示形式。要说明这一点:这不是浮点数的整数值。将其用作计算中的整数将产生意外的结果。但是您可以访问整数的位,就像访问浮点数的位一样

2) 尾数面具和指数面具,它们是做什么用的

浮点数由指定尾数(数字字符串)的位数和表示数字“位置”的指数部分表示。将浮点数“转换”为整数类型后,这两部分混合在整数值中<代码>尾数遮罩
和指数遮罩(你的Q中有一个输入错误)遮罩了那些部分<代码>尾数位将指数移动到正确的位置

3) 在这里使用
&

是位和运算符屏蔽了位

让我们举一个完全虚拟的例子:

代码中有23位尾数和8位指数。32位中的一位保留给符号。让我们有一个数字:

00000001000010011010011010101010
有1个符号位,8个指数位和23个尾数位,你可以这样读

0 00100010 00010011010011010101010
s exponent --------mantissa-------
要获取尾数,请使用仅设置尾数位的掩码:

0 00000000 11111111111111111111111
当使用位和它时,两个操作数中只有1的位为1,每隔一位为0:

0 00100010 00010011010011010101010 A
0 00000000 11111111111111111111111 B
- -------- -----------------------
0 00000000 00010011010011010101010 A&B
尾数与指数(现在是表示尾数的实整数值)分离

要获得指数,首先将整个单词右移,使指数从第0位(最右边)开始:

要将指数与符号位隔离,必须对其重新进行位和位:

00000000000000000000000 0 00100010 A
00000000000000000000000 0 11111111 B
------------------------------------
00000000000000000000000 0 00100010 A&B

Et voíla.

您的代码采用的格式是IEEE Std 754-2008(C标准使用IEC 60559,与之相同)第3.4节中所述的“二进制交换浮点格式”,甚至还有一个图表(图3.1)

对于32位浮点,它是

bits:     0        1-9         10-32
      sign bit   exponent     significant (or mantissa)
正如Jens Gustedt在他的评论中解释的那样,工会曾经劝说编译器允许浮点用作int(大小相同!),反之亦然。一旦你有了整数,你就可以处理位了

符号位是最左边的位,您可以通过除以2^31或向右移动31得到它

指数在以下8位中。代码通过右移有效位大小的23位并屏蔽指数(不包括符号位)来获取指数

它们通过屏蔽最右边的23位来获得有效值

指数本身是有偏差的。为什么?您希望数字0=1具有正指数。指数不是为符号添加额外的位,而是减半。低于某个限制的所有内容(偏差)必须将其视为负数,将其视为正数。要获得具有正确值的指数符号,只需减去偏差即可

该标准定义了一些特殊值:
Inf
NaN
(信令
NaN
和静音
NaN
),它们被编码为

  • NaN
    (IEEE标准754-2008第6.2.1节)
所有二进制NaN位字符串都将偏置指数字段E的所有位设置为1(见3.4)。安静的NaN位字符串应使用第一位(d1)进行编码尾随有效位字段T的值为1。应使用尾随有效位字段的第一位为0对信号NaN位字符串进行编码。如果尾随有效位字段的第一位为0,则尾随有效位字段的其他一些位必须为非零,以区分NaN和无穷大。在首选编码中,仅描述ribed,应通过将d1设置为1使信号NaN静音,保留T unchan的剩余位
bits:     0        1-9         10-32
      sign bit   exponent     significant (or mantissa)
double frexp(double x, int *eptr)
{
  int sign, exponent;
  int i;

  /*
   * The exponent of an IEEE-754 double (binary64) is an 11-bit large integer
   */
  double ap_2[11] = {
    2.0000000000000000000000000000000000000,
    4.0000000000000000000000000000000000000,
    16.000000000000000000000000000000000000,
    256.00000000000000000000000000000000000,
    65536.000000000000000000000000000000000,
    4294967296.0000000000000000000000000000,
    18446744073709551616.000000000000000000,
    3.4028236692093846346337460743176821146e38,
    1.1579208923731619542357098500868790785e77,
    1.3407807929942597099574024998205846128e154,
    1.7976931348623157e308  // DBL_MAX
  };

  double ap_half[11] = {
    0.50000000000000000000000000000000000000,
    0.25000000000000000000000000000000000000,
    0.062500000000000000000000000000000000000,
    0.0039062500000000000000000000000000000000,
    1.5258789062500000000000000000000000000e-5,
    2.3283064365386962890625000000000000000e-10,
    5.4210108624275221700372640043497085571e-20,
    2.9387358770557187699218413430556141946e-39,
    8.6361685550944446253863518628003995711e-78,
    7.4583407312002067432909653154629338374e-155,
    5.5626846462680034577255817933310101606e-309    // < DBL_MIN
  };

  if (isinf(x)) {
    *eptr = 0;
    return x;
  }
  if (isnan(x)) {
    *eptr = 0;
    return x;
  }

  if (x == 0.0) {
    *eptr = 0;
    return x;
  }

  exponent = 0.0;
  /*
   * Easier to work with positive values
   */
  if (x < 0) {
    x = -x;
    sign = 1;
  }

  else {
    sign = 0;
  }

  if (x >= 1.0) {
    /*
     * Big steps
     */
    for (i = 0; x >= ap_2[i]; i++) {
      exponent += (1 << i);
      x *= ap_half[i];
    }
    /*
     * Small steps
     */
    if (x < 0.5) {
      while (x < 0.5) {
        x *= 2.0;
        exponent--;
      }
    } else {
      while (x > 1.0) {
        x /= 2.0;
        exponent++;
      }
    }
  } else {
    /*
     * Same as above, but in the opposite direction
     */
    for (i = 0; x < ap_half[i]; i++) {
      exponent -= (1 << i);
      x *= ap_2[i];
    }
    if (x < 0.5) {
      while (x < 0.5) {
        x *= 2.0;
        exponent--;
      }
    } else {
      while (x > 1.0) {
        x /= 2.0;
        exponent++;
      }
    }
  }

  if (sign) {
    x = -x;
  }
  *eptr = exponent;
  return x;
}
int isinf(double x){
   // TODO: not every compiler might eat this check for Inf
   // GCC-4.8.4  does
   // TCC 0.9.25 does
   // clang 3.4-1ubuntu3 (based on LLVM 3.4) does
   return (x == 1.0/0.0 || x == -1.0/0.0);
}


int isnan(double x){
   return (x != x);
}