📄
Henry
  • Henry的神秘小屋
  • 其他
    • 字节码与机器码的区别?
  • ObjectiveC
    • NSInvocation
    • 检测OC项目中未使用的方法
    • Method Selector
    • 消息转发
  • Swift
    • 检测Swift项目中未使用的类 方法 属性
    • NSCoding
    • Mirror
    • JSONEncode JSONDecode
    • Any AnyObject AnyClass
    • is as? as! as
  • Cocoapods
    • 看懂Podfile.lock文件
    • 在编写的Pod中使用宏预编译
  • iOS
    • 苹果应用 保持画面流畅
    • 关于计算机展示图像的一些问题
    • @testable
    • iOS中URLRequest的缓存策略
    • CodePush接入流程
    • H5在WKWebView中读取沙盒文件
    • FDLog App客户端日志系统
    • 如何实现JSBridge基于WKWebView
    • 网络请求各个指标的度量
    • iOS13 UIModalPresentationStyle
    • 实现H5离线包机制
    • NSURLProtocol 拦截器
    • Framework
    • Lock
    • CFNetwork NSURLSession NSURLConnection
    • setNeedsLayout layoutIfNeeded layoutSubviews
    • StackView
    • Flutter Method Channel:从Dart到Native调用链
    • JSONSerialization.ReadingOptions
    • JSONSerialization.WritingOptions
    • RunLoop高级2
    • RunLoop高级1
    • RunLoop中级
    • RunLoop初级
    • LineBreak AutoShrink
    • 如何给H5出WebView调试包
    • TODO、FIXME、!!!、???、MARK
    • Operation的使用
    • UserDefault
    • 了解WKWebView
    • 输出日志信息到系统控制台
    • Float Double 失去精度问题
    • 使用xcodebuild命令打包导出
    • 在iOS项目中使用C++定义的模块
    • 证书问题
    • 创建常驻线程
  • 源码
    • 阅读PINCache源码
    • 解读AspectHook
    • HandyJSON是如何实现的?
  • 汇编
    • 看懂汇编
由 GitBook 提供支持
在本页
  • 结论先行
  • 先看个例子

这有帮助吗?

  1. iOS

Float Double 失去精度问题

结论先行

  • 浮点数计算不精确并不是 bug,因为标准就是这样的。

  • 原因简单来说是这样:2 进制的小数无法精确的表达 10 进制小数,计算机在计算 10 进制小数的过程中要先转换为 2 进制进行计算,这个过程中出现了误差。

  • 解决方法:对于需要精确结果的场景,别直接使用浮点数进行计算。使用 Decimal 类型。

先看个例子

print(Double(6.6) + Double(1.3))
print(Float(6.6) + Float(1.3))
print(Decimal(6.6) + Decimal(1.3))

// 输出: 
// 7.8999999999999995
// 7.8999996
// 7.9

从上面的结果我们可以看到 Double 和 Float 类型计算 6.6 + 1.3 的时候 结果会和我们预期不一样。 至于为什么会导致这种问题,经过翻阅一些文章和资料,我在下面话题给出答案。

为什么会导致这种情况么

要解释这个问题,就得从 10 进制与 2 进制的转换说起。

十进制数字在二进制中表示,众所周知,计算机做运算是使用二级制的。所以其实咱们在程序中做 10 进制运算,都是要转换为 2 进制再进行计算的。

10 进制整数转换为 2 进制的方法可能大家都知道:

除以 2,商继续除以 2,得到 0 为止,将余数逆序排列 例如: 22 / 2 11 余 0 11 / 2 5 余 1 5 / 2 2 余 1 2 / 2 1 余 0 1 / 2 0 余 1 所以 22 的的二进制是 10110

那 10 进制小数转换为 2 进制的方法呢:

乘以 2,取整,小数部分继续乘以 2,取整,得到小数部分 0 为止,将整数顺序排列 0.8125 x 2 1.625 取 1 0.625 x 2 1.25 取 1 0.25 x 2 0.5 取 0 0.5 x 2 1.0 取 1 所以 0.8125 的二进制是 0.1101

那么问题就来了,比如你想计算10进制0.2的2进制:

0.2 x 2 0.4 0.4 x 2 0.8 0.8 x 2 1.6 0.6 x 2 1.2 0.2 x 2 0.4 ……

发现了吗?它乘不尽,是无限循环的……

而 swift 使用 64 位双精度浮点数存储数字,类似科学计数法,其中 1 位用来存储符号,11 位用来存储指数值,52 位用来存储尾数值(真正的数字),当计算的结果的二进制有效位数超过 52 位时,就会出现精度丢失的问题……

怎么处理

Swift 中已经内置了 Decimal 类型,当计算金钱的时候建议使用 Decimal 来保证精度问题

print(Double(6.6) + Double(1.3))
print(Float(6.6) + Float(1.3))
print(Decimal(6.6) + Decimal(1.3))

// 7.8999999999999995
// 7.8999996
// 7.9
上一页输出日志信息到系统控制台下一页使用xcodebuild命令打包导出

最后更新于5年前

这有帮助吗?

本文参考:

http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html