我是靠谱客的博主 勤劳麦片,这篇文章主要介绍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.

经过

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*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; }

然后,编译运行.

复制代码
1
2
3
4
5
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

神奇的一幕发生了.
结果竟然不一样.
于是我只得

复制代码
1
2
D:codecppexamplefloat_precision>gcc main.c -S

看汇编吧,希望我能看懂.

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
.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++内容请搜索靠谱客的其他文章。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部