本篇翻译的原英文在: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. python异常捕获异常堆栈输出

    python异常捕获异常堆栈输出 学习了:https://blog.csdn.net/chris_grass/article/details/77927902 import traceback def ...

  2. poj(1011)——Sticks(经典的dfs+剪枝)

    题目的大致意思是: 如今有n根木棍,然后须要把它们拼成相同长度的木棍,问满足这个条件的最短的长度是多少? 想法嘛:那肯定是dfs把长度搜一遍就好,但问题的关键是这里会超时.那么就要用到剪枝的原理了. ...

  3. angular1.x 组件开发

    搜索框组件开发: 1.注册组件 app.js angular.module("myApp",[]) .component("nameSearch",{ temp ...

  4. Android中View自己定义XML属性具体解释以及R.attr与R.styleable的差别

    为View加入自己定义XML属性 Android中的各种Widget都提供了非常多XML属性,我们能够利用这些XML属性在layout文件里为Widget的属性赋值. 例如以下所看到的: <Te ...

  5. vmware下安装mac os虚拟机问题,最后还是最终攻克了被一个小失误给陷害了

    今天决定来体验一下苹果系统.虚拟机文件大概用了一天半时间才下载完毕,解压后是39G大小,赶紧安装VMWARE.然后载入虚拟机文件体验.開始当我苹果标志出来的时候,我以为成功了.但是那个小齿轮一直在转, ...

  6. Git多账号登陆

        最近工作上遇到了使用git+repo的情况,需要用公司的邮箱和账号名重新申请ssh公私密钥,而我本身在github上也有一些开源项目,这里就是记录一下我是如何实现git多账号登陆的.   取消 ...

  7. 网络请求--Retrofit2用法

    欢迎Follow我的GitHub, 关注我的CSDN. Retrofit是Square开发的网络请求库, 简化了网络请求的使用, 这个库太知名了, 优点我就不多说了. 让我们看看怎样使用吧? 注意: ...

  8. Posix消息队列相关函数

    Posix消息队列(message queue) IPC函数中常用的参数取值: 打开或创建POSIX IPC对象所用的各种oflag常值o_RDONLY   只读O_WRONLY   只写O_RDWD ...

  9. win10安装Anaconda+TensorFlow+配置PyCharm

    其实很简单,我这里也只是记录一下而已. 第一大坑:anaconda必须安装4.2以前的版本,不能安装4.3以后的 版本:满满的血泪史 因为我们需要安装自带的python必须是3.5,才可以调用Tens ...

  10. C++中extern “C”含义深层探索(在原作的基础上修改) .

    1. 引言 C++ 语言的创建初衷是“a better C” ,但是这并不意味着C++ 中类似C 语言的全局变量和函数所采用的编译和连接方式与C 语言完全相同.作为一种欲与C 兼容的语言,C++ 保留 ...