示例代码
#include "stdio.h"
#include <stdint.h>
int main()
{
double d1 = 210.01;
uint32_t m = 1000;
uint32_t v1 = (uint32_t) (d1 * m);
printf("%d",v1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出
1.使用-m32选项进行编译时(即gcc -g3 -m32 test.c)
/test 174 # ./a.out
210009
Run Code Online (Sandbox Code Playgroud)
2.使用-m64选项进行编译时(即gcc -g3 -m64 test.c)
test 176 # ./a.out
210010
Run Code Online (Sandbox Code Playgroud)
为什么我会有所不同?
我的理解"是", m将被提升为双倍,乘法将向下推unit32_t.此外,由于我们使用stdint类型整数,我们将进一步消除与架构等相关的歧义.
我知道这里有些东西可疑,但不能把它钉住.
更新:
只是为了澄清(对于其中一个评论),gcc和g ++都可以看到上述行为.
Ant*_*ala 10
我可以在我的gcc(Ubuntu 5.2.1-22ubuntu2)上确认结果.似乎发生的是32位未优化代码使用387 FPU和FMUL操作码,而64位使用SSE MULS操作码.(只需gcc -S test.c使用不同的参数执行并查看汇编程序输出).正如众所周知,在387 FPU执行该FMUL有更多的精度(80!)超过64位,这样看来,便将不同在这里.当然,原因是64位IEEE double的确切值210.01不是那个,而是
210.009999999999990905052982270717620849609375
Run Code Online (Sandbox Code Playgroud)
当你乘以1000时,你实际上并不只是移动小数点 - 毕竟没有小数点,而是浮点值中的二进制点 ; 所以价值必须四舍五入.在64位双打上,它被四舍五入.在80位387 FPU寄存器上,计算更精确,最终向下舍入.
在阅读了这篇文章之后,我相信gcc在32位arch上生成的结果并不符合标准.因此,如果你强制标准C99或C11用-std=c99,-std=c11你会得到正确的结果
% gcc -m32 -std=c11 test.c; ./a.out
210010
Run Code Online (Sandbox Code Playgroud)
如果您不想强制使用C99或C11标准,您也可以使用该 -fexcess-precision=standard开关.
不过有趣并不止于此.
% gcc -m32 test.c; ./a.out
210009
% gcc -m32 -O3 test.c; ./a.out
210010
Run Code Online (Sandbox Code Playgroud)
因此,如果您使用编译,则会得到"正确"的结果-O3; 这当然是因为64位编译器使用64位SSE数学来对计算进行常数倍.
要确认额外的精度会影响它,您可以使用long double:
#include "stdio.h"
#include <stdint.h>
int main()
{
long double d1 = 210.01; // double constant to long double!
uint32_t m = 1000;
uint32_t v1 = (uint32_t) (d1 * m);
printf("%d",v1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在甚至-m64将其舍入到210009.
% gcc -m64 test.c; ./a.out
210009
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
686 次 |
| 最近记录: |