概述
C++ double 浮点精度问题?
起因
早上起来玩CF,发现竟然是一个浮点数问题! 真的好害怕.
题目条件说输入的参数
c
+
m
+
p
=
1
c+m+p=1
c+m+p=1,然后我就在代码里加上了assert(c+m+p==1.)
,结果就是Runtime error.
经过
反复测试,反复修改,我将代码精简如下.
/*D:codecppexamplefloat_precision>type main.c*/
#include <assert.h>
typedef unsigned char bool;
bool directCompare(double a,double b,double c,double r) {
return a+b+c==r;
}
bool answerCompare(double a,double b,double c,double r) {
double ans=a+b+c;
return ans==r;
}
int main() {
assert(
directCompare(0.4,0.2,0.4,1.) == answerCompare(0.4,0.2,0.4,1.)
);
return 0;
}
然后,编译运行.
D:codecppexamplefloat_precision>gcc main.c -o a
D:codecppexamplefloat_precision>a
Assertion failed: directCompare(0.4,0.2,0.4,1.) == answerCompare(0.4,0.2,0.4,1.), file main.c, line 18
神奇的一幕发生了.
结果竟然不一样.
于是我只得
D:codecppexamplefloat_precision>gcc main.c -S
看汇编吧,希望我能看懂.
.file "main.c"
.text
.globl _directCompare
.def _directCompare; .scl 2; .type 32; .endef
_directCompare:
pushl %ebp
movl %esp, %ebp
subl $32, %esp
movl 8(%ebp), %eax
movl %eax, -8(%ebp)
movl 12(%ebp), %eax
movl %eax, -4(%ebp)
movl 16(%ebp), %eax
movl %eax, -16(%ebp)
movl 20(%ebp), %eax
movl %eax, -12(%ebp)
movl 24(%ebp), %eax
movl %eax, -24(%ebp)
movl 28(%ebp), %eax
movl %eax, -20(%ebp)
movl 32(%ebp), %eax
movl %eax, -32(%ebp)
movl 36(%ebp), %eax
movl %eax, -28(%ebp)
fldl -8(%ebp)
faddl -16(%ebp)
faddl -24(%ebp)
fldl -32(%ebp)
fucomip %st(1), %st
setnp %al
movl $0, %edx
fldl -32(%ebp)
fucomip %st(1), %st
fstp %st(0)
cmovne %edx, %eax
leave
ret
.globl _answerCompare
.def _answerCompare; .scl 2; .type 32; .endef
_answerCompare:
pushl %ebp
movl %esp, %ebp
subl $48, %esp
movl 8(%ebp), %eax
movl %eax, -24(%ebp)
movl 12(%ebp), %eax
movl %eax, -20(%ebp)
movl 16(%ebp), %eax
movl %eax, -32(%ebp)
movl 20(%ebp), %eax
movl %eax, -28(%ebp)
movl 24(%ebp), %eax
movl %eax, -40(%ebp)
movl 28(%ebp), %eax
movl %eax, -36(%ebp)
movl 32(%ebp), %eax
movl %eax, -48(%ebp)
movl 36(%ebp), %eax
movl %eax, -44(%ebp)
fldl -24(%ebp)
faddl -32(%ebp)
faddl -40(%ebp)
fstpl -8(%ebp)
fldl -8(%ebp)
fldl -48(%ebp)
fucomip %st(1), %st
fstp %st(0)
setnp %al
movl $0, %edx
fldl -8(%ebp)
fldl -48(%ebp)
fucomip %st(1), %st
fstp %st(0)
cmovne %edx, %eax
leave
ret
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC4:
.ascii "main.c "
.align 4
LC5:
.ascii "directCompare(0.4,0.2,0.4,1.) == answerCompare(0.4,0.2,0.4,1.) "
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp
movl %esp, %ebp
pushl %ebx
andl $-16, %esp
subl $32, %esp
call ___main
fld1
fstpl 24(%esp)
fldl LC2
fstpl 16(%esp)
fldl LC3
fstpl 8(%esp)
fldl LC2
fstpl (%esp)
call _directCompare
movl %eax, %ebx
fld1
fstpl 24(%esp)
fldl LC2
fstpl 16(%esp)
fldl LC3
fstpl 8(%esp)
fldl LC2
fstpl (%esp)
call _answerCompare
cmpb %al, %bl
je L6
movl $18, 8(%esp)
movl $LC4, 4(%esp)
movl $LC5, (%esp)
call __assert
L6:
movl $0, %eax
movl -4(%ebp), %ebx
leave
ret
.section .rdata,"dr"
.align 8
LC2:
.long -1717986918
.long 1071225241
.align 8
LC3:
.long -1717986918
.long 1070176665
.ident "GCC: (tdm-1) 5.1.0"
.def __assert; .scl 2; .type 32; .endef
终于看懂了,浮点数加减法使用的是x87 FPU指令.而该计算环境使用的80位的浮点精度,在数据传输时将double
存储的64位自动转化为80位,然后计算完再转换回来.
directCompare
在FPU上进行比较,而answerCompare
比较之前将数据转换为64位,然后又转换为80位,产生了精度损失(?结果却算对了).
结果
看了
-
intel的文档:
- Intel® 64 and IA-32 architectures software developer’s manual volume 1: Basic architecture
- Intel® 64 and IA-32 architectures software developer’s manual combined volumes 2A, 2B, 2C, and 2D: Instruction set reference, A- Z
-
gdb的文档:
- GDB User Manual
-
了解了:
- gdb命令:
- ni: 下一条指令
- si: 进入call
- set confirm off: k,q不提示
- set disassemble-next-line
- x/nfu: 检查内存
- print: 显示
- $ps: program status register
- x86指令:
- setnp: set if not PF
- cmovne: move if not equal
- X87 FPU 指令:
- fld1: 加载1到%st
- fld: 加载数据到%st
- fadd: 加数据到%st
- fucomip: unordered 比较%st(0) %st(i) 弹栈
- fstp: 将%st的数据装入寄存器中 弹栈
- gdb命令:
最后
以上就是勤劳麦片为你收集整理的c++ double 浮点精度问题(gcc 编译)?C++ double 浮点精度问题?的全部内容,希望文章能够帮你解决c++ double 浮点精度问题(gcc 编译)?C++ double 浮点精度问题?所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复