为什么两个计算会得出不同的答案?

4
#include <stdio.h>

#define pi1 3.141

const float pi = 3.141;

int main()    
{    
    printf("%f %f", 4 * 10 * pi, 4 * 10 * pi1); 
}

输出(在我的机器上)为125.639999 125.640000

你得到了什么输出? - Corley Brigman
输出(在我的机器上)为 125.639999 125.640000 - Joseph Quinsey
我的上一个评论使用了 gcc -03。如果我去掉 -O3,我得到的是 125.640001 125.640000。gcc 版本是 4.1.2。 - Joseph Quinsey
3个回答

5

pi1 是一个预处理符号,会在文本中被替换为一个双精度浮点数。

pi 是一个从双精度浮点数初始化的浮点常量,因此会失去一些精度位(请参阅IEEE754规范)。

更多详情,实际上作为浮点数存储的 pi 是 0x40490625,即 3.1410000324249267578125。pi1 存储为 0x400920C49BA5E354,即 3.1410000000000000142108547152。


如果pi作为float大于3.141,那么为什么printf4*40*pi转换为125.639999,暗示一个低于125.64的值? - chqrlie

2

在C语言中,浮点数常量的类型为double,除非你添加后缀f(float)或L(long double)

因此,在第一部分(4*10*pi)中,3.141是一个double值,被转换为float精度并存储在const float pi中,然后表达式4*10*pi将在float精度下进行计算。另一方面,在4*10*pi1中,该宏直接被替换为字符串,然后从double中计算,这就是为什么结果不同的原因。

请参见floating constant


0

只是为了强调@Joel和@Lưu Vĩnh Phúc的回答,考虑以下代码片段

#include<stdio.h>

#define pi1 3.141

const float pi = 3.141;

int main()
{

printf("foo"); // Just to mark assembly output

printf("4*10*pi == %f\n", 4*10*pi);

printf("4*10*pi1 == %f\n", 4*10*pi1);

float a = 4*10*pi;
float b = 4*10*pi1;
printf("%f %f\n", a, b);

}

而gcc(4.8.1)生成的汇编代码:

    .file   "c.c"
    .globl  _pi
    .section .rdata,"dr"
    .align 4
_pi:
    .long   1078527525
    .def    ___main;    .scl    2;  .type   32; .endef
LC0:
    .ascii "foo\0"
LC3:
    .ascii "4*10*pi == %f\12\0"
LC5:
    .ascii "4*10*pi1 == %f\12\0"
LC7:
    .ascii "%f %f\12\0"
    .text
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
LFB6:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $48, %esp
    call    ___main
    movl    $LC0, (%esp)
    call    _printf

    flds    LC1
    flds    LC2
    fmulp   %st, %st(1)
    fstpl   4(%esp)
    movl    $LC3, (%esp)    
    call    _printf

    fldl    LC4
    fstpl   4(%esp)
    movl    $LC5, (%esp)
    call    _printf

    flds    LC1
    flds    LC2
    fmulp   %st, %st(1)
    fstps   44(%esp)
    movl    LC6, %eax
    movl    %eax, 40(%esp)
    flds    40(%esp)
    flds    44(%esp)
    fxch    %st(1)
    fstpl   12(%esp)
    fstpl   4(%esp)
    movl    $LC7, (%esp)
    call    _printf

    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE6:
    .section .rdata,"dr"
    .align 4
LC1:
    .long   1078527525
    .align 4
LC2:
    .long   1109393408
    .align 8
LC4:
    .long   -1030792151
    .long   1079994613
    .align 4
LC6:
    .long   1123764142
    .ident  "GCC: (GNU) 4.8.1"
    .def    _printf;    .scl    2;  .type   32; .endef

我添加了额外的printfs以帮助定位问题。现在看看这些值是如何以不同的方式构建的:

对于4*10*pi,从FPUs中加载了两个浮点数,而对于4*10*pi1只加载了一个(没有乘法)。我相信这将导致已经提到的浮点/双精度转换问题。

我不是汇编专家,但我认为这些片段可以帮助理解正在发生的事情,或者至少澄清浮点乘法的两种方法不会导致相同的汇编。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接