博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浮点运算潜在的结果不一致问题
阅读量:7285 次
发布时间:2019-06-30

本文共 1278 字,大约阅读时间需要 4 分钟。

昨天阿楠发现了项目中的一个 bug ,是因为浮点运算的前后不一致导致的。明明是完全相同的 C 代码,参数也严格一致,但是计算出了不相同的结果。我对这个现象非常感兴趣,仔细研究了一下成因。

原始代码比较繁杂。在弄清楚原理后,我简化了出问题的代码,重现了这个问题:

static voidfoo(float x) {    float xx = x * 0.01f;    printf("%d\n", (int)(x * 0.01f));    printf("%d\n", (int)xx);}intmain() {    foo(2000.0f);    return 0;}

使用 gcc 4.9.2 ,强制使用 x87 浮点运算编译运行,你会发现令人诧异的结果。

gcc a.c -mfpmath=3871920

前一次的输出是 19 ,后一次是 20 。

这是为什么呢?让我们来看看 gcc 生成的代码,我截取了相关的段落:

flds    16(%rbp)    flds    .LC0(%rip)    fmulp   %st, %st(1)    fstps   -4(%rbp)          ; 1. x * 0.01f 结果保存到内存中的 float 变量中    flds    16(%rbp)    flds    .LC0(%rip)    fmulp   %st, %st(1)    fisttpl -20(%rbp)        ; 2. x * 0.01f 结果直接转换为整型    movl    -20(%rbp), %eax    movl    %eax, %edx    leaq    .LC1(%rip), %rcx    call    printf    flds    -4(%rbp)                 ; 3. 读出 1. 保存的乘法结果    fisttpl -20(%rbp)    movl    -20(%rbp), %eax    movl    %eax, %edx    leaq    .LC1(%rip), %rcx    call    printf

这里我做了三行注释。

首先,0.01 是无法精确表示成 2 进制的,所以 * 0.01 这个操作一定会存在误差。

两次运算都是 x * 0.01f ,虽然按 C 语言的转换规则,表达式中都是 float 时,按 float 精度运算。但这里 gcc 生成的代码并没有严格设置 FPU 的精度控制,在注释 2 这个地方,乘法结果是直接从浮点寄存器转换为整数的。而在注释 1 这个地方,把乘法结果通过 fstps 以低精度形式保存到内存,再在注释 3 的地方 flds 读回。

所以在注释 2 和注释 3 的地方,浮点寄存器 st 内的值其实是有差别的,这导致了 fisttpl 转换为整数后结果不同。

转载于:https://www.cnblogs.com/lancidie/p/7467366.html

你可能感兴趣的文章
分布式系统的事务处理
查看>>
VC 双缓存技术+滚动条
查看>>
strtol详解
查看>>
mysql部分参数注解
查看>>
Powershell常用命令总结
查看>>
HAProxy+Keepalived实现Web服务器负载均衡
查看>>
apache动静态编译
查看>>
导出到Excal表格
查看>>
nginx Rewrite 规则
查看>>
周珍:浅析百度调整的几大猜想
查看>>
微软异想天开!居然想让电脑厂商为它生产VR眼镜
查看>>
Linux Mint和LMDE将开发新版
查看>>
Django框架下admin.py的中文修改+xadmin中文修改
查看>>
Linux CentOS 7 设置开机运行级别为3(文本多用户级别)
查看>>
“WPF老矣,尚能饭否”—且说说WPF今生未来(上):担心
查看>>
利用jpinyin将汉字转化成拼音
查看>>
Python之第一个程序
查看>>
习题总结(二)——禁ctrl+alt+delete,禁普通用户登录,禁ping
查看>>
localStorage只能存储字符串
查看>>
【Spring Boot】11.使用docker安装常见服务
查看>>