首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >浮点表示(使用按位运算符)

浮点表示(使用按位运算符)
EN

Stack Overflow用户
提问于 2016-08-07 15:27:26
回答 2查看 2.9K关注 0票数 1

这是一个问题的解决方案,打印浮点的表示(即:X=(−1)^符号·(1.m22m21m20)。。。(M_0)·2^(e -−偏差)我还没有理解其中的一些内容:( 1)联合的使用,为什么? 2) EXPONENET_MASK和−,它们的用途是什么? 3)在这里的用法:

代码语言:javascript
复制
  uint32_t exponent = ( t.bits >> MANTISSA_WIDTH ) & EXPONENT_MASK;
  uint32_t mantissa = ( t.bits  &  MANTISSA_MASK );

下面是代码:

代码语言:javascript
复制
#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;
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-08-07 16:11:52

1)工会的使用,为什么?

位运算符仅适用于积分类型。由于明显的原因,不能将浮点数转换为整数。但是,一个联合定位组件重叠的内存。因此,通过写入浮点分量,然后读取积分分量,返回浮点数的积分表示。要说明这一点:这不是浮点数的整数。在计算中使用它作为一个整数会得到意想不到的结果。但是您可以访问整数的位,就像浮点数的位一样。

2) MANTISSA_MASK和EXPONENET_MASK,它们是干什么用的?

浮点数由指定尾数(数字字符串)的若干位和表示数字的“位置”的指数部分表示。在将浮点数“转换”为整型后,这两个部分在积分值中混合。MANTISSA_MASKEXPONENT_MASK (您的Q中有一个错误)屏蔽了这些部件。MANTISSA_BITS将指数移动到正确的位置。

3) &在这里的使用:

是位和运算符遮住了比特。

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

从您的代码中,您有23位尾数和8位指数。32位中的一位是为标志保留的。让我们有一个数字:

代码语言:javascript
复制
00000001000010011010011010101010

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

代码语言:javascript
复制
0 00100010 00010011010011010101010
s exponent --------mantissa-------

要获得尾数,需要使用只设置尾数位的掩码:

代码语言:javascript
复制
0 00000000 11111111111111111111111

当您-并且它,只有在两个操作数中为1的位是1时,其他位是0:

代码语言:javascript
复制
0 00100010 00010011010011010101010 A
0 00000000 11111111111111111111111 B
- -------- -----------------------
0 00000000 00010011010011010101010 A&B

尾数与指数分离(现在是表示尾数的实际整数值)。

要得到指数,首先右移整个单词,以便指数从位0开始(最右):

代码语言:javascript
复制
0 00100010 00010011010011010101010 
00000000000000000000000 0 00100010 >> 23 (mantissa bist)

要将指数从符号位中分离出来,你必须再来一次:

代码语言:javascript
复制
00000000000000000000000 0 00100010 A
00000000000000000000000 0 11111111 B
------------------------------------
00000000000000000000000 0 00100010 A&B

等等。

票数 1
EN

Stack Overflow用户

发布于 2016-08-07 17:01:12

您的代码采用的格式是第三点四节中描述的“二进制交换浮点格式”(C标准使用IEC 60559,它是相同的),甚至还有一个图表(图3.1)。

对于32位浮点数,它是

代码语言:javascript
复制
bits:     0        1-9         10-32
      sign bit   exponent     significant (or mantissa)

正如Jens Gustedt在他的评论中所解释的那样,联合使用说服编译器允许一个用作int的浮点数(大小相同!)反之亦然。一旦你有一个整数,你就可以与比特杂耍。

符号位是最左边的位,你可以把它除以2^31,或者向右移动31。

指数在以下8位。代码通过向右移动意义大小的23位并掩盖指数(不包括符号位)来获得它。

他们通过掩蔽最右边的23位来获得意义。

指数本身是有偏的。为什么?您希望数字0= 1表示正指数。指数不是为符号加一个额外的位,而是减半。任何低于某一限度的事物(偏见)都必须被认为是消极的,而高于某一限度的则被认为是积极的。要用正确的值得到指数的符号,只需减去偏差。

该标准定义了一些特殊的值:InfNaN (信令、NaNNaN),它们被编码为

  • NaN (IEEESTD754-2008第6.2.1节)

所有二进制NaN位字符串都将偏置指数字段E的所有位设置为1(参见3.4)。一个安静的NaN位串应该用尾随意义的第一位(d1)编码,并且字段T是1;信令NaN位串应该以尾随意义的第一位和字段为0进行编码。如果后继意义和字段的第一位为0,则尾随意义字段的其他位必须为非零,才能区分NaN和无穷大。在刚才描述的首选编码中,信令NaN应通过将d1设置为1来保持安静,使剩余的T比特保持不变。 对于二进制格式,有效载荷被编码在p-2最小意义和尾随重要字段的位中。

  • Inf Inf的编码不是在IEEE-754 (或者我还没有找到)中以这样一种显式的方式描述的,而是只用于3.5.2节中的十进制编码,但它通常是最大指数(所有位都设置为1),是一个不改变的符号位,用于区分正负无穷大和所有意义的位,并设置为0来区分它与任何有限数。很容易测试。

代码中的位杂耍非常复杂,假设有特定的endianess,并且floatuint32_t具有相同的endianess,并且float是按照IEEEST754-2008/IEC 60559中描述的单一精度格式编码的(您需要用C标准宏__STDC_IEC_559__来检查它),并且union技巧可以与所使用的编译器一起工作。如果你需要像frexp(3)这样的东西,你真的应该使用内置。

frexp() (对于double,懒得重写它)。它来自我自己版本的lib计量,因为只需要少量的函数,内存也很稀疏)假设的要少得多,只有浮点数符合IEC 60559:

代码语言:javascript
复制
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;
}

函数isinf()有点小,怎么说呢,粗体的,也不是所有的编译器都可能支持它:

代码语言:javascript
复制
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);
}

我将2的倍数的复杂内联计算(正如我前面提到的:内存稀疏)替换为两个表。我希望我没有通过这样做破坏其余的代码。

我跟往常一样太慢了。这一次被阿明·内格姆-阿瓦德击败43分钟。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38815660

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档