> For the complete documentation index, see [llms.txt](https://yongpenglovemimi123.gitbook.io/henry/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yongpenglovemimi123.gitbook.io/henry/ios/float-double-shi-qu-jing-du-wen-ti.md).

# Float Double 失去精度问题

## 结论先行

* 浮点数计算不精确并不是 bug，因为标准就是这样的。
* 原因简单来说是这样：2 进制的小数无法精确的表达 10 进制小数，计算机在计算 10 进制小数的过程中要先转换为 2 进制进行计算，这个过程中出现了误差。
* 解决方法：对于需要精确结果的场景，别直接使用浮点数进行计算。使用 Decimal 类型。

## 先看个例子

```swift
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` 来保证精度问题

```swift
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
```

本文参考： <http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html>
