本篇翻译的原英文在: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. linux内核中预留4M以上大内存的方法

    在内核中, kmalloc能够分配的最大连续内存为2的(MAX_ORDER-1)次方个page(参见alloc_pages函数,     "if (unlikely(order >= ...

  2. R语言入门视频笔记--9--随机与数据描述分析

    古典概型的样本总量是一定的,且每种可能的可能性是相同的, 1.中位数:median(x) 2.百分位数:quantile(x)或者quantile(x,probe=seq(0,1,0.2)) #后面这 ...

  3. MongoDB增删改查操作详解(命令行)

    一.插入 MongoDB的插入操作很简单,使用insert方法,这里演示从创建数据库.创建集合到插入文档.查询文档. 集合创建方法参数说明: size:集合最大空间 max:集合最多文档数量 (超出s ...

  4. Flutter学习(一)——搭建开发环境(Windows)

    久闻 Flutter 大名,今天终于有时间体验一下了 ٩(๑>◡<๑)۶ 官网:https://flutter.dev/ 中文官网:https://flutterchina.club/ 一 ...

  5. 使用icomoon把svg图片生成字体图标

    今天看了使用icomoon来将svg转换成图标字体,本来是不会使用别人给的svg,也不清楚具体的好处是什么,查了svg以后,越来越懵,svg挺好的为什么要转成图标字体呢. 一.SVG介绍 SVG 是一 ...

  6. 第2章 CentOS网络配置

    一.配置说明 1.1 说明 1.本文以VM仅本机模式下与宿主机的网络配置为例. 1.2 达成目标 1.Windows能ping通centOS 2 centOS能够ping通Windows 3 cent ...

  7. 微信小程序,不同的输入框显示

    <!--pages/index/Component/TextInput/TextInput.wxml--> <view class="viewTitle"> ...

  8. Go -- 今日头条架构

    夏绪宏,今日头条架构师,专注对高性能大规模 Web 架构,云计算.性能优化.编程语言理论等方向,PHP committer,HHVM 项目贡献者.2009 加入百度,先后从事大规模 IDC 自运维设施 ...

  9. 【HDOJ 5399】Too Simple

    pid=5399">[HDOJ 5399]Too Simple 函数映射问题 给出m函数 里面有0~m个函数未知(-1) 问要求最后1~n分别相应仍映射1~n 有几种函数写法(已给定的 ...

  10. Unity3d中制作异步Loading进度条所遇到的问题

    背景 通常游戏的主场景包括的资源较多,这会导致载入场景的时间较长.为了避免这个问题,能够首先载入Loading场景.然后再通过Loading场景来载入主场景. 由于Loading场景包括的资源较少,所 ...