我是靠谱客的博主 勤劳麦片,最近开发中收集的这篇文章主要介绍c++ double 浮点精度问题(gcc 编译)?C++ double 浮点精度问题?,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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位,然后计算完再转换回来.
directCompareFPU上进行比较,而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的数据装入寄存器中 弹栈

最后

以上就是勤劳麦片为你收集整理的c++ double 浮点精度问题(gcc 编译)?C++ double 浮点精度问题?的全部内容,希望文章能够帮你解决c++ double 浮点精度问题(gcc 编译)?C++ double 浮点精度问题?所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(72)

评论列表共有 0 条评论

立即
投稿
返回
顶部