为什么在大多数编程语言中 0.1 + 0.2 不等于 0.3?

简介: 为什么在大多数编程语言中 0.1 + 0.2 不等于 0.3?

前言


在文章开始之前先看下面“诡异”的一幕。

a, b = 0.1, 0.2
print(a + b == 0.3)
print(a + b)
out:
False
0.30000000000000004
复制代码

0.1 + 0.2 == 0.3 结果竟然为 False ?不知道大家第一次见到这个场景作何感想,反正我是有点怀疑人生,为什么会产生这样的结果呢,下面详细看一下。


浮点数的限制


浮点数在计算机硬件中表示为一个以 2 为基数(二进制)的小数。我们先看看如果用十进制和二进制来表示0.125(10)。

十进制

0.125(10)

等于

1\times10^{-1} + 2\times10^{-2} + 5\times10^{-3} = \cfrac{1}{8}

>**二进制**<br> >0.001(2)<br> >$$ 0\times2^{-1} + 0\times2^{-2} + 1\times2^{-3} = \cfrac{1}{8}

这两个小数均表示 0.125(10),唯一真正的区别是第一个是以 10 为基数的小数表示法,第二个则是 2 为基数。


不幸的是,大多数的十进制小数都不能精确地表示为二进制小数,但有些浮点数也能够用二进制精确的表述,条件是位数有限且分母能表示成 2^n 的小数。如 0.5, 0.125。这将导致在大多数情况下,你输入的十进制浮点数都只能近似地以二进制浮点数形式储存在计算机中。


正如上文中的 0.1 ,我们手动计算一下它的二进制结果。

注:十进制整数转二进制方法:除2取余;十进制小数转二进制方法:乘2除整

计算过程:

0.1 * 2 = 0.2 # 0
0.2 * 2 = 0.4 # 0
0.4 * 2 = 0.8 # 0
0.8 * 2 = 1.6 # 1
0.6 * 2 = 1.2 # 1
0.2 * 2 = 0.4 # 0
0.4 * 2 = 0.8 # 0
.....
复制代码


从上面结果可以看出,0.1 的二进制为:

0.0001100110011001100110011001100110011001100110011...
复制代码

这是一个二进制无限循环小数,但计算机内存有限,我们不能储存所有的小数位数。那如何解决呢?

答案就是从末尾某个位置截断,直接取近似值,因此,在目前大部分编程语言(支持处理器浮点运算)中,浮点数都只能近似地使用二进制小数表示。


很多人使用 Python 的时候都不会意识到这个差异的存在,因为 Python 只会输出计算机中存储的二进制值的十进制近似值。但我们要牢记,即使输出的结果看起来好像就是 0.1 的精确值,实际储存的值只是最接近 0.1 的计算机可表示的二进制值。


解决方式


1.decimal

decimal 模块可以进行十进制数学计算,我们将浮点数转成字符串进行运算。

from decimal import Decimal
a, b = Decimal('0.1'), Decimal('0.2')
a + b == Decimal('0.3')
out:True
复制代码


2.numpy.float32

numpy 模块中的32为浮点型保存数据。

import numpy as np
temp = np.array([0.1, 0.2, 0.3], dtype=np.float32)
temp[0] + temp[1] == temp[2]
复制代码


当然体高精度的同时,性能可能会降低,在实际应用中这些近似值造成的细微偏差可能不会造成什么影响。如果碰到了留个心眼就好!

说了这么多,总结出一句话就是:浮点数转二进制时丢失了精度,计算完再转回十进制时和理论结果不同。不知道大家get到了吗?



相关文章
|
5月前
|
C++
在C++语言中比较两个数的大小的方法
在C++语言中比较两个数的大小的方法
1018 0
|
3月前
|
存储 缓存 前端开发
编程语言中值函数表示的优化
【7月更文挑战第7天】这段文本是关于编程语言实现中值的表示和优化的总结,特别是讨论了一个叫做OTao的语言。文本最后鼓励读者探索编程语言设计的更多方面,并提供了进一步学习的资源和建议。
39 2
编程语言中值函数表示的优化
|
4月前
|
存储 Dart 编译器
Dart编程语言中的数值类型与运算
Dart编程语言中的数值类型与运算
105 0
|
5月前
|
JavaScript 前端开发 Java
运算符优先级在不同编程语言中的差异
运算符优先级在不同编程语言中的差异
36 1
|
4月前
|
存储
为什么大多数编程语言下标都从0开始
为什么大多数编程语言下标都从0开始
|
5月前
|
Java Go C++
Rust每日一练(leetDay0023) 二进制求和、左右对齐、平方根
Rust每日一练(leetDay0023) 二进制求和、左右对齐、平方根
46 0
Rust每日一练(leetDay0023) 二进制求和、左右对齐、平方根
|
5月前
|
Swift
在Swift编程语言中,有符号整数类型
在Swift编程语言中,有符号整数类型
46 2
|
11月前
|
存储 Rust 编译器
Rust 基础入门 ——数值类型
Rust 提供了一个非常简洁的方式,用来生成连续的数值,例如 1..5,生成从 1 到 4 的连续数字,不包含 5 ;1..=5,生成从 1 到 5 的连续数字,包含 5,它的用途很简单,常常用于循环中:
59 0
|
Python
Python编程 数值类型 数学计算
Python编程 数值类型 数学计算
11394 0
|
Rust JavaScript 前端开发
【Rust 课外知识】0.1+0.2为什么不等于0.3(下)
【Rust 课外知识】0.1+0.2为什么不等于0.3(下)
【Rust 课外知识】0.1+0.2为什么不等于0.3(下)