这是打印浮点表示的问题的解决方案(即:x =( - 1)^符号·(1.m 22 m 21 m 20 ... m 0)·2 ^(e -bias))我还没有理解其中的一些内容: 1)使用联盟,为什么? 2)MANTISSA_MASK和EXPONENET_MASK,它们用于什么? 3)使用&在这里:
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;
}
答案 0 :(得分:1)
1)使用联盟,为什么?
位运算符仅适用于整数类型。出于显而易见的原因,您无法将浮点数转换为整数。但是联合会找到重叠的组件的内存。因此,通过写入浮点组件然后读取积分组件返回浮点数的整数表示。为了清楚起见:这是不浮点数的整数值。在计算中将其用作整数将产生意外结果。但是你可以访问整数的位,因为它是浮点数的位。
2)MANTISSA_MASK和EXPONENET_MASK,它们的用途是什么?
浮点数由指定尾数(数字串)的位数和表示数字“位置”的指数部分表示。在将浮点数“转换”为整数类型之后,将这两个部分混合在积分值中。 MANTISSA_MASK
和EXPONENT_MASK
(你的Q中有拼写错误)掩盖了这些部分。 MANTISSA_BITS
将指数移动到正确的位置。
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位开始(最右边):
0 00100010 00010011010011010101010
00000000000000000000000 0 00100010 >> 23 (mantissa bist)
要将指数与符号位隔离,您必须再次进行位操作:
00000000000000000000000 0 00100010 A
00000000000000000000000 0 11111111 B
------------------------------------
00000000000000000000000 0 00100010 A&B
Etvoíla。
答案 1 :(得分:1)
您的代码假定的格式是“二进制交换浮点格式”,如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&lt; n&lt; 1表示负指数,数字> = 1表示正指数。而不是为符号添加额外的位,指数减半。低于一定限度(偏差)的一切都必须被认为是消极的,而一切都是正面的。要使用正确的值得到指数的符号,您只需减去偏差。
标准定义了一些特殊值:Inf
和NaN
(信号NaN
和安静NaN
),编码为
NaN
(IEEE Std 754-2008第6.2.1节)所有二进制NaN位串都将偏置指数字段E的所有位设置为1(见3.4)。应该用尾随有效位字段T的第一位(d1)编码安静的NaN位串。应当用尾随有效位字段的第一位为0来编码信令NaN位串。如果第一位尾随有效位字段为0,尾随有效位字段的其他位必须为非零以区分NaN和无穷大。在刚刚描述的优选编码中,通过将d1设置为1来保持信令NaN,使T的其余位保持不变。
对于二进制格式,有效载荷在尾随有效字段的p-2个最小有效位中进行编码。
Inf
Inf
的编码在IEEE-754中没有以明确的方式描述(或者我还没有找到),仅适用于3.5.2节中的十进制编码,但它是通常是最大指数(所有位设置为1),一个未更改的符号位,用于区分正负无穷大,有效位的所有位设置为0,以区别于任何有限数。轻松测试。代码中的位杂乱非常复杂,假设具有特定的字节顺序,float
和uint32_t
具有相同的字节顺序,float
以单精度格式编码如IEEE Std 754-2008 / IEC 60559中所述(您需要使用C标准宏__STDC_IEC_559__
进行检查)并且union
技巧适用于所使用的编译器。如果您需要frexp(3)
之类的内容,那么您真的应该使用内置版本。
一个frexp()
(对于一个double
,懒得重写它。它来自我自己的libmath版本,因为只需要少量函数而内存很稀疏)更少,只是浮点数符合IEC 60559:
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()
有点,我该如何设置,粗体,并非所有编译器都支持它:
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的倍数替换了复杂的内联计算(如前所述:内存稀疏)。我希望通过这样做不会破坏其余的代码。
Aaaaa和我一如既往地太慢了。这一次被Amin Negm-Awad击败了43分钟。