浮点表示的问题
Issue with floating point representation
int x=25,i;
float *p=(float *)&x;
printf("%f\n",*p);
我知道浮点数和 int 的位表示是不同的,但无论我存储什么值,答案总是 0.000000。根据浮点表示,它不应该是其他值吗?
您的代码有未定义的行为 -- 但它很可能会按照您的预期运行,只要类型 int
和 float
的大小和对齐方式兼容。
使用“%f”格式打印 *p
,您丢失了很多信息。
试试这个:
#include <stdio.h>
int main(void) {
int x = 25;
float *p = (float*)&x;
printf("%g\n", *p);
return 0;
}
在我的系统上(也可能在你的系统上),它打印:
3.50325e-44
int
值 25
的大部分高位为零。这些位可能与 float
类型的指数字段位于同一位置 - 导致数字非常小。
查找 IEEE 浮点表示以获取更多信息。字节顺序将成为一个问题。 (不要在实际代码中做这种事情,除非你有 非常 充分的理由。)
正如 rici 在评论中建议的那样,了解浮点表示的更好方法是从浮点值开始,将其转换为相同大小的无符号整数,并以十六进制显示整数值.例如:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void show(float f) {
unsigned int rep;
memcpy(&rep, &f, sizeof rep);
printf("%g --> 0x%08x\n", f, rep);
}
int main(void) {
if (sizeof (float) != sizeof (unsigned int)) {
fprintf(stderr, "Size mismatch\n");
exit(EXIT_FAILURE);
}
show(0.0);
show(1.0);
show(1.0/3.0);
show(-12.34e5);
return 0;
}
出于讨论的目的,我们假设 int
和 float
都是 32 位宽。我们还将假设 IEEE-754 浮动。
浮点值表示为<em>sign</em> * β<sup><em>exp</em></sup> * <em>符号</em>
。对于 32 位二进制浮点数,β
是 2
,指数 exp
范围从 -126
到 127
, significand 是 normalized 二进制小数,因此在小数点之前有一个前导非零位。例如25
的二进制整数表示为
11001<sub>2</sub>
而 25.0
的二进制浮点表示为:
1.1001<sub>2</sub> * 2<sup>4</sup> // normalized
32 位浮点数的 IEEE-754 编码是
s eeeeeeee fffffffffffffffffffffff
其中 s
表示符号位,e
表示指数位,f
表示有效数(小数)位。指数使用 "excess 127" 表示法进行编码,这意味着指数值 127
(01111111<sub>2</sub>
) 表示 0
,而 1
(00000001<sub>2</sub>
) 表示 -126
和 254
(11111110<sub>2</sub>
)表示127
。有效数的前导位未明确存储,因此 25.0
将被编码为
0 10000011 10010000000000000000000 // exponent 131-127 = 4
但是,当您将 32 位 整数 值 25
的 位模式 映射到 32 位时会发生什么位浮点格式?我们得出以下结果:
0 00000000 00000000000000000011001
事实证明,在IEEE-754浮点数中,指数值00000000<sub>2</sub>
被保留用于表示0.0
和次正规(或非正规)数字。次正规数是接近 0
的数,不能表示为 1。??? * 2<sup><em>exp</em></sup>
,因为指数必须小于我们可以用 8 位编码的指数。这些数字被解释为 0。??? * 2<sup>-126</sup>
,根据需要有尽可能多的前导 0
。
这样的话,加起来就是0.00000000000000000011001<sub>2</sub> * 2<sup>-126</sup>
,这给了我们 3.50325 * 10<sup>-44</sup>
。
您必须映射大整数值(超过 2<sup>24</sup>
)才能看到除 0 以外的任何值一堆小数位。而且,正如 Keith 所说,无论如何这都是未定义的行为。
int x=25,i;
float *p=(float *)&x;
printf("%f\n",*p);
我知道浮点数和 int 的位表示是不同的,但无论我存储什么值,答案总是 0.000000。根据浮点表示,它不应该是其他值吗?
您的代码有未定义的行为 -- 但它很可能会按照您的预期运行,只要类型 int
和 float
的大小和对齐方式兼容。
使用“%f”格式打印 *p
,您丢失了很多信息。
试试这个:
#include <stdio.h>
int main(void) {
int x = 25;
float *p = (float*)&x;
printf("%g\n", *p);
return 0;
}
在我的系统上(也可能在你的系统上),它打印:
3.50325e-44
int
值 25
的大部分高位为零。这些位可能与 float
类型的指数字段位于同一位置 - 导致数字非常小。
查找 IEEE 浮点表示以获取更多信息。字节顺序将成为一个问题。 (不要在实际代码中做这种事情,除非你有 非常 充分的理由。)
正如 rici 在评论中建议的那样,了解浮点表示的更好方法是从浮点值开始,将其转换为相同大小的无符号整数,并以十六进制显示整数值.例如:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void show(float f) {
unsigned int rep;
memcpy(&rep, &f, sizeof rep);
printf("%g --> 0x%08x\n", f, rep);
}
int main(void) {
if (sizeof (float) != sizeof (unsigned int)) {
fprintf(stderr, "Size mismatch\n");
exit(EXIT_FAILURE);
}
show(0.0);
show(1.0);
show(1.0/3.0);
show(-12.34e5);
return 0;
}
出于讨论的目的,我们假设 int
和 float
都是 32 位宽。我们还将假设 IEEE-754 浮动。
浮点值表示为<em>sign</em> * β<sup><em>exp</em></sup> * <em>符号</em>
。对于 32 位二进制浮点数,β
是 2
,指数 exp
范围从 -126
到 127
, significand 是 normalized 二进制小数,因此在小数点之前有一个前导非零位。例如25
的二进制整数表示为
11001<sub>2</sub>
而 25.0
的二进制浮点表示为:
1.1001<sub>2</sub> * 2<sup>4</sup> // normalized
32 位浮点数的 IEEE-754 编码是
s eeeeeeee fffffffffffffffffffffff
其中 s
表示符号位,e
表示指数位,f
表示有效数(小数)位。指数使用 "excess 127" 表示法进行编码,这意味着指数值 127
(01111111<sub>2</sub>
) 表示 0
,而 1
(00000001<sub>2</sub>
) 表示 -126
和 254
(11111110<sub>2</sub>
)表示127
。有效数的前导位未明确存储,因此 25.0
将被编码为
0 10000011 10010000000000000000000 // exponent 131-127 = 4
但是,当您将 32 位 整数 值 25
的 位模式 映射到 32 位时会发生什么位浮点格式?我们得出以下结果:
0 00000000 00000000000000000011001
事实证明,在IEEE-754浮点数中,指数值00000000<sub>2</sub>
被保留用于表示0.0
和次正规(或非正规)数字。次正规数是接近 0
的数,不能表示为 1。??? * 2<sup><em>exp</em></sup>
,因为指数必须小于我们可以用 8 位编码的指数。这些数字被解释为 0。??? * 2<sup>-126</sup>
,根据需要有尽可能多的前导 0
。
这样的话,加起来就是0.00000000000000000011001<sub>2</sub> * 2<sup>-126</sup>
,这给了我们 3.50325 * 10<sup>-44</sup>
。
您必须映射大整数值(超过 2<sup>24</sup>
)才能看到除 0 以外的任何值一堆小数位。而且,正如 Keith 所说,无论如何这都是未定义的行为。