本篇翻译的原英文在:http://mauve.mizuumi.net/2013/06/16/desyncs-and-fpu-synchronization/#more-725(可能要FQ)

如果你曾经处理过跨系统的同步问题的话,那么碰到不同步的问题也就见怪不怪了。其中的一些是比较容易处理的,但是另外一些则可能让你连续几周抓狂。

当然,在这些问题中,最令人头疼的就是浮点数了。即使是0.000000001的误差也会很快地累积膨胀,导致更大的问题。传统的做法是在与逻辑相关的部分不要使用浮点数,但这种做法并不是任何场景都可行的。如果你了解在不同场合下浮点数带来的陷阱,那么使用浮点并保持完全的同步也是可能的。

先快速描述下浮点数的工作方式。以单精度浮点数为例,存储由三部分组成,1bit的符号位,8bit的指数部分,23bit的尾数部分。实际的数据大小为:sign*value*pow(2,exponent)。这意味着如果你超出了value所限定的大小范围((2^24)-1,即16777215),你将会丧失一定精度,而只会保留较大量级的数据。与此相较,双精度浮点数共64位,包含11位的指数部分和52位的尾数部分,有更大的容差空间。

这里需要注意的是数据不能被2的次幂整除的情况,这种情况下只能用无理数表示。0.1f+0.2f并不精确等于0.3f,实际上0.1f+0.2f的计算结果为0.300000004470(译注:可以做下测试,大同小异),因为其中任一者都不能被2的次幂整除。出于这个原因,千万不要在在对浮点数做了数学运算后又比较相等性(译注:我在LUA交互命令行上测试时:print(0.3==0.3) => true; print(0.1+0.2==0.3) => false; LUA上是双精度浮点)。

当你意识到浮点常量在编译时会被编译器转化成二进制形式,而且可能还会有轻微的差别时,事情就更有趣了。不过至目前为止我还没有碰到过这种情况,即使碰到了,我也不会感到有啥惊讶的。

浮点内部存储(INTERNAL FLOAT STORAGE)

这是处理不同步时要考虑的第一个点。内部存储的形式并不是将数据加载进寄存器那样。X86平台的系统使用了基于栈的格式来表示要被处理的操作指令,当你把数据从内存加载入寄存器时它会按照当前的内部精度模式来运行,而不管它原先的模式应该是什么样子的。正常来说这是没有什么害处的,而且还会有那么一丁点的精准度提升,但是由于内部存储的类型在不同平台上是未定义的,便成为不可回避的问题。

幸运的是,补救的措施相对简单:X86处理器可以设定精度模式,定义被处理数据的限制范围。这点可以通过汇编指令FLDCW来做到,而且Windows上面还有_controlfp/_control78函数可用。当你需要FPU同步时一定要记得做一步,而且保证它不会被取消掉。在你的逻辑处理流程之前运行FINIT和FLDCW指令不会影响到程序的正确运行。

注意很多高级语言比如C#,是不允许你设置精度模式的,因为其是平台无关的。在这种情况下,你就不能指望浮点数在不同平台上有完全相同的表现了。

不是所有事情都是标准化的(NOT EVERYTHING IS STANDARDIZED)

下一个问题是虽然IEEE754浮点标准规定了一系列的流程,但是有相当部分的常用库函数是没有定义的。通常对于结果应该是什么样子都会有一些建议,但是没有要求严格遵循。需要特别留意的是有一些超验函数(transcendental functions),或者是任何与三角学有关的函数,比如计算Sine和Cosine。

这意味着如果你需要一致性的话,那就不能随心所欲地使用数学库中的三角函数了。不同平台上的舍入差异带来的错误总是不可避免的,而且不管你把精度扩大多少,都不能把你从微小误差的传播放大的泥淖中拯救出来。考虑这样一种情况,一个在特定方向上的舍入边界的值(a value that exists right on the boundary of being rounded in a specific direction),在不同的处理器上可能会被区别对待。没有标准的严格说明,没有人能告诉你结果会是什么样。最佳答案是,由你自己定制sin/cos的变种实现来保持一致性,也许会慢一点,但毕竟是可靠的。

说到舍入不得不提的是,fabs是没有任何问题的。这的确令人惊讶---开平方根的计算有明确定义的舍入标准,因此可以放心使用。

编译器太聪明(COMPILERS TOO SMART FOR YOUR OWN GOOD)

现代处理器和编译器非常智能高效,但这也意味着如果开发者不太注意的话,很容易忽略掉它们一些太过聪明的做法。

举个例子,一个特别的优化是把几个简单的操作合并成一个复杂的指令,在当前场合反而带来了麻烦。比如,Multiply-Accumulate operation会把数学运算构造:a=b+(c*d)转化成一条指令。这种混合的作法只有一个舍入步骤,与把这些运算拆开单独来算是不同的。这个就不好了。你最好把所有这种类似于此的操作全部禁用掉。其它类似于此的"智能"优化依然是有的,比如把a*(b+1)转换成(a*b)+a,虽然减少了代码量,但是却产生了不同的计算结果。从debug版本转到release版本时,优化器可能会比你想象得要更智能,所以一定要小心谨慎。

VC++有一个fp:strict math mode可以禁用上述优化,GCC如果不打开-ffast-math选项那也一切OK。尽管如此,留意一下输出的汇编代码依然是值得的。

第三方库以及环境因素(THIRD PARTY LIBRARIES AND ENVIRONMENTAL ISSUES)

是时候把这些东西从你的应用程序中整个儿地清理出去了,甚至包括你所依赖的方方面面。嵌入式脚本语言,比如Lua,非常频繁地把浮点数作为它们的默认数据类型。你需要尽可能地控制这部分的使用。像上述提到的C#,作为在设计上平台无关的语言,是不会让你直接控制这些的(不清楚JAVA如何,但我估计也是这样的)。

最后,最好的办法也许是在业务逻辑中不要使用浮点数,只使用整数或是定点数。只是把简单地做做数值乘法而不考虑乘/除的话,用整数模拟高精度数是完美的做法。但是记住你依然摆脱不了精度限制!

不管是以怎样的方式,真诚希望这篇文章能帮助到那些被同样问题所困的人。

FPU同步(翻译)的更多相关文章

  1. AI翻译离无障碍交流有多远

    AI翻译服务通过硬件.软件连接千千万万个应用场景,会打破语言不通的尴尬局面吗?会是人工翻译的终结者吗? 世界这么大,我想去看看!十一长假临近,梦想中的你背起行囊,自由行走在异国的大街小巷.然而现实的画 ...

  2. ReactMix框架,让你实现一套js代码,基于ReactNative在H5,App都能完美跑起来,Write Once,Run Anywhere

    ReactNative框架推出已经有一段时间了,相信很多小伙伴都在尝试实现Write Once, Run Anywhere的梦想,比如淘宝的ReactWeb等等,但是这些框架都局限于因为ReactNa ...

  3. 12小时包你学会基于ReactMix框架的ReactNativeApp开发(一)Hello World!

    ReactMixhttps://github.com/xueduany/react-mix自从昨天发布起来,得到了不少小伙伴的热捧,很高兴帮助大家解决了憋在心中很久的问题“如果我只会HTML,Css, ...

  4. TGL站长关于常见问题的回复

    问题地址: http://www.thegrouplet.com/thread-112923-1-1.html 问题: 网站配有太多的模板是否影响网站加载速度 月光答复: wp不需要删除其他的模板,不 ...

  5. Python爬取CSDN博客文章

    0 url :http://blog.csdn.net/youyou1543724847/article/details/52818339Redis一点基础的东西目录 1.基础底层数据结构 2.win ...

  6. (c#)SKYPE API项目总结(一)

    原文:(c#)SKYPE API项目总结(一) 这个项目的需求:SKYPE软件文字聊天同步翻译,并将翻译后的内容会发送给对方,将对方发给自己的话翻译成自己语种.功能见图:               ...

  7. 如何从 0 开始学 Ruby on Rails

    如何从 0 开始学 Ruby on Rails (漫步版)Ruby 是一门编程语言,Ruby on Rails 是 Ruby 的一个 web 框架,简称 Rails. 有很多人对 Rails 感兴趣, ...

  8. javascript面向对象编程笔记

    对象:一切事物皆是对象.对象是一个整体,对外提供一些操作.比如说一个收音机是一个对象,我们不需要知道它的内部结构是什么,只需要会使用外部的按钮就可以使用收音机. 面向对象:面向对象语言的标志是他们都有 ...

  9. HTML入门9

    这一篇着眼于HTML里的音频和视频标签及相关处理: 传统技术不能再web中使用音频和视频,一致使用Flash后来因为一些HTML/CSS特性,安全问题,慢慢退出.在HTML5提出后,新特性<vi ...

随机推荐

  1. Java学习:一 开篇

    呃 工作中要用到Android开发,呃 不巧的是,关于Java关于Android,当初也只是浅浅的了解了一下.....真是书到用时方恨少了.. 趁现在工作不是太忙,还是花点时间来学习一下吧. 写写博客 ...

  2. Laravel 静态资源管理

    <link rel="stylesheet" href="{{ asset('bootstrap/css/bootstrap.min.css') }}" ...

  3. LeetCode:926. 将字符串翻转到单调递增

    暴力法超时:思想:动态规划 public int minFlipsMonoIncrb(String S) { int result = S.length(); for (int i = 0; i &l ...

  4. SQL视图优化改写为存储过程遇到 双引号 单引号问题

    核心在于拼接SQL字符串中遇到中文双引号问题:   可以使用系统函数 替换掉set @pageStr =   replace(@queryStr,'"','''')  不过更推荐 使用两个单 ...

  5. ASP.NET Core 如何记录每次响应的Response信息 - sky 胡萝卜星星 - CSDN博客

    原文:ASP.NET Core 如何记录每次响应的Response信息 - sky 胡萝卜星星 - CSDN博客 上一篇文章中我们已经成功的记录了Request部分的信息,现在我们来看下如何记录Res ...

  6. centos安装python的虚拟环境和虚拟管理环境

    一.大部分介绍的方式是使用pip安装:1.pip install virtualenv     2.pip install virtualenvwrapper 但是我安装完,并没有相应的命令:virt ...

  7. es删除文档或者删除索引

    es删除文档或者删除索引 学习了:https://www.imooc.com/video/15771 删除文档: DELETE http://127.0.0.1:9200/people/man/1 删 ...

  8. cocos2d-x改底层之获取UIListView的实际内容大小

    实际项目中UI界面中常常会用到UIListView.大多会在CocoStudio中直接加入这个控件. 可是在使用中发现了一些坑和功能缺乏,然后就看了一下底层的逻辑,发现略微改一下底层就能够满足需求,所 ...

  9. Solidworks如何创建投影曲线

    画好草图之后(草图是在上视基准面上画的)然后点击曲线,投影曲线   面选择要投影的曲面,然后就得到了平面曲线在曲面上的投影得到的空间曲线   注意这种方法对于开环轮廓也是可以用的,比如下面,我定义一个 ...

  10. Collection接口和Collections类的简单区别和讲解

    这里仅仅进行一些简单的比较,如果你想要更加详细的信息话,请自己百度. 1.Collection: 是集合类的上层接口.本身是一个Interface,里面包含了一些集合的基本操作. Collection ...