本篇翻译的原英文在: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. 小窥React360——用React创建360全景VR体验

    前言    混迹VR届的发烧友兼开发者们一定不要错过这款FaceBook推出的跨端VR开发框架——React360,称为360全景体验框架更为准确,因为其前身是FaceBook和Oculus2017年 ...

  2. HDU - 5584 LCM Walk (数论 GCD)

    A frog has just learned some number theory, and can't wait to show his ability to his girlfriend. No ...

  3. Spring的Hello World工程

    通过Spring的Hello World工程研究以下几个点: 0.如何创建工程及引入依赖. 1.通过Spring的beans.xml实现依赖注入,动态创建实例. 2.了解Spring的工作原理. 具体 ...

  4. java文本文件加密解密类

    原文:http://www.open-open.com/code/view/1420031154765 import java.awt.*; import java.awt.event.*; impo ...

  5. PHP网站渗透中的奇技淫巧:检查相等时的漏洞

    PHP是现在网站中最为常用的后端语言之一,是一种类型系统 动态.弱类型的面向对象式编程语言.可以嵌入HTML文本中,是目前最流行的web后端语言之一,并且可以和Web Server 如apache和n ...

  6. outlook 2010 自动密送Email

    以下功能请勿非法使用: 密抄到多人这个需要用到宏 方法一: 1.在Outlook里面键入ALT+F11打开VBA编辑器 2.展开“Project (VbaProject.OTM)/Microsoft ...

  7. Android菜单menu控件大全

    下载:http://www.see-source.com/androidwidget/list.html?type=16 Android-NewPopupMenu 使用PopupWindow实现的Po ...

  8. [cocos2d-x]怎样降低cocos2d-x游戏的耗电量?

    Cocos2d-x游戏的耗电量一直是个让人头疼的问题,一个简单的三消游戏,玩一会手机就热得发烫,更郁闷的是电池消耗非常快.基本上两个小时就能够把电池耗光. 近期又看到一个帖子.有个老外用cocos2d ...

  9. reorder-list——链表、快慢指针、逆转链表、链表合并

    Given a singly linked list L: L0→L1→…→Ln-1→Ln,reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… You must do thi ...

  10. 学习Android之第六个小程序新浪微博(二)(ListView和TabActivity)

    效果图例如以下: 选项卡的使用: 1.继承TabActivity 2.声明TabHost变量,通过方法getTabHost()获取并赋值. (TabHost  tabHost =getTabHost( ...