c++ double 浮点精度问题(gcc 编译)?

C++ double 浮点精度问题?

起因

早上起来玩CF,发现竟然是一个浮点数问题! 真的好害怕.
题目条件说输入的参数 c + m + p = 1 c+m+p=1 c+m+p=1,然后我就在代码里加上了assert(c+m+p==1.),结果就是Runtime error.

经过

反复测试,反复修改,我将代码精简如下.

/*D:\code\cpp\example\float_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:\code\cpp\example\float_precision>gcc main.c -o a

D:\code\cpp\example\float_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:\code\cpp\example\float_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\0"
	.align 4
LC5:
	.ascii "directCompare(0.4,0.2,0.4,1.) == answerCompare(0.4,0.2,0.4,1.)\0"
	.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位,然后计算完再转换回来.
directCompareFPU上进行比较,而answerCompare比较之前将数据转换为64位,然后又转换为80位,产生了精度损失(?结果却算对了).

结果

看了

猜你喜欢

转载自blog.csdn.net/agctXY/article/details/118656886