计算机里的0.1+0.2等于0.3么

在多数编程语言中,当你计算 0.1 + 0.2 的结果时,会发现并不是想象中的 0.3,而是 0.30000000000000004。这是为什么呢?
有人会回答是精度问题,那精度是怎样导致的呢?为什么编程语言不去处理这些问题呢?

正整数的二进制很好理解,1 是 1,2 是 10, 3 是 11,如此类推。而小数的二进制是 0.1 代表十进制的 0.5,0.11 代表 (1/2 + 1/4) = 0.75,0.01 代表 (0 + 1/4) = 0.25,0.111 代表 (1/2 + 1/4 + 1/8) = 0.875,0.101 代表 (1/2 + 0 + 1/8) = 0.625,如此类推。
由此我们可以发现,在整数的世界里,二进制位数的增加意味着我们能表示的整数范围越来越大,且严丝合缝。而小数的二进制位数增加,带来的改变是跳跃性的。除非你要的小数是 2 的 -n 次方,不然你只能不断趋近这个值,而不能准确表达它。
那么,我们假设用 16 位来表示十进制的 0.1 呢,它是 0.0001100110011001,而这个二进制小数的真实值其实是 0.0999908447265625。0.2 类似,它的16 位二进制是 0.0011001100110011,它的真实值是 0.1999969482421875。而它俩相加值自然其实是 0.29998779296875 了。
因此在计算机中 0.1 + 0.2 为什么不等于 0.3 就很好理解了。至于为什么上面的结果和开头的 0.30000000000000004 不一致,只是精度造成的,我举例里只用了 16 位,而比如 Java 的 double 是 53 位。

那么有人会想了,小数部分如果能设计成十进制,是不是所有问题都解决了呢?并不是,比如试试用十进制表示 1/3,再用它乘 3,一样会有类似的问题。
当然,如果要模拟十进制的效果实现精准计算,各种编程语言也提供了类似的方法,比如 Java 的 BigDecimal 类。那为啥编程语言不让它成为默认的方式呢,自然是因为二进制更契合计算机硬件,运算更快。

本文为即兴科普文,没有详细说明每个知识点。比如读者可能会发现不同的编程语言运算结果出奇地一致,这是因为它们遵循了 IEEE 754 标准。
再比如 Java 的 double 存储长度其实是 64 位,为什么精度只有 53 位呢?也是因为该标准定义的是 1 位表示符号、52 位表示尾数、11 位表示指数。不过这样算是 52 位啊,为什么精度是 53 位呢,是因为该浮点数格式首位恒为 1,为了空间利用率没有将其存储,就多了一位可以用于表示精度,这个概念叫次正规数。具体就不展开谈了。
参考资料:《Java编程的逻辑》。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇