#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Почему два вычисления дают разные ответы?
#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Почему два вычисления дают разные ответы?
Интересно. Предоставьте распечатку, а также параметры вашего компилятора. Моя догадка: pi1 вставляется препроцессором посредством замены строки. Компилятор выбирает это и заменяет его постоянным числом во время компиляции. Однако он не выбирает ту же оптимизацию с помощью «pi», возможно, в зависимости от уровня оптимизации, который вы применяете. Таким образом, первое число вычисляется во время выполнения, что может привести к чему-то слабому.
Как правило, никогда не ожидайте равенства от двух поплавков, всегда используйте эпсилон для их сравнения.
pi1 является символом препроцессора и заменяется текстовым как двойной.
pi - постоянная поплавка, инициализированная двойным, и, следовательно, потеряет некоторые прецизионные биты (см. Спецификации IEEE754).
Для получения более подробной информации, pi как поплавок фактически хранится как 0x40490625, который является 3.1410000324249267578125. pi1 хранится в 0x400920C49BA5E354, который 3,1410000000000000142108547152
C Константы с плавающей точкой имеют тип двойной, если не присоединить суффикс F (с плавающей точкой) или LF (длинный двойной)
Так 3,141 усекается до 3.141f и хранить к pi, то pi
в свою очередь продвигается от float до double в выражении 4*10*pi
, тогда как 4*10*pi1
подставляется непосредственно в виде строки и затем вычисляется из double. Вот почему он отличается
Просто, чтобы укрепить то, что @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.
Я не эксперт по ассемблеру, но я думаю, что эти фрагменты могут помочь понять, что происходит или по крайней мере прояснить, что два подхода к умножению поплавка не приводят к одной и той же сборке.
Какой выход вы получили? –
Вывод (на моей машине) '125.639999 125.640000' –
Мой предыдущий комментарий использовал' gcc -03'. Если я *** опустил *** -O3', я получаю '125.640001 125.640000'. Версия gcc - 4.1.2. –