2014-01-31 5 views
3
#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Почему два вычисления дают разные ответы?

+0

Какой выход вы получили? –

+0

Вывод (на моей машине) '125.639999 125.640000' –

+0

Мой предыдущий комментарий использовал' gcc -03'. Если я *** опустил *** -O3', я получаю '125.640001 125.640000'. Версия gcc - 4.1.2. –

ответ

-3

Интересно. Предоставьте распечатку, а также параметры вашего компилятора. Моя догадка: pi1 вставляется препроцессором посредством замены строки. Компилятор выбирает это и заменяет его постоянным числом во время компиляции. Однако он не выбирает ту же оптимизацию с помощью «pi», возможно, в зависимости от уровня оптимизации, который вы применяете. Таким образом, первое число вычисляется во время выполнения, что может привести к чему-то слабому.

Как правило, никогда не ожидайте равенства от двух поплавков, всегда используйте эпсилон для их сравнения.

6

pi1 является символом препроцессора и заменяется текстовым как двойной.

pi - постоянная поплавка, инициализированная двойным, и, следовательно, потеряет некоторые прецизионные биты (см. Спецификации IEEE754).

Для получения более подробной информации, pi как поплавок фактически хранится как 0x40490625, который является 3.1410000324249267578125. pi1 хранится в 0x400920C49BA5E354, который 3,1410000000000000142108547152

2

C Константы с плавающей точкой имеют тип двойной, если не присоединить суффикс F (с плавающей точкой) или LF (длинный двойной)

Так 3,141 усекается до 3.141f и хранить к pi, то pi в свою очередь продвигается от float до double в выражении 4*10*pi, тогда как 4*10*pi1 подставляется непосредственно в виде строки и затем вычисляется из double. Вот почему он отличается

0

Просто, чтобы укрепить то, что @Joel и @ ответы LUU Виньфук, рассмотрим следующий фрагмент кода

#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, чтобы помочь найти вещи. Теперь посмотрим, как значения te сконструированы по-другому:

Для 4*10*pi два поплавка загружаются из FPU и для 4*10*pi1 только один (умножения нет). Я считаю, что это приведет к уже упомянутым проблемам с float/double conversion.

Я не эксперт по ассемблеру, но я думаю, что эти фрагменты могут помочь понять, что происходит или по крайней мере прояснить, что два подхода к умножению поплавка не приводят к одной и той же сборке.

Смежные вопросы