美文网首页
从 50% + 50% = 0.75 说起:我儿子的信仰崩塌了

从 50% + 50% = 0.75 说起:我儿子的信仰崩塌了

作者: 华山令狐冲 | 来源:发表于2024-02-22 08:40 被阅读0次

笔者之前的文章 【开箱】知乎社区 2024 年新年礼盒介绍了知乎每年的台历,其中有这样一页我觉得很有意思,它也再次让我儿子,领教了计算机和人类在完成一个任务上的处理差异。

问题:为什么手机计算器上,50% + 50% = 0.75?

我以前从没留意过这个问题。在三星手机上试了一下,还真是这样:

知乎上这个问题的回复:

因为手机计算器(大部分情况下的默认计算器),都按照 a% + b% = a + ab% 或 a(1+b%)计算。

当你输入 50% + 50% 的时候,手机先会把前面一个 50% 转化成 0.5(因为它的前面没有数了,于是就默认转成小数,a% = a/100),后一个就理解为“加上前一个数的 50%”,于是 50% + 50% = 50% + 50% * 50% = 50% + 25% = 75% = 0.75.

手机计算器的这种处理方式,让我儿子觉得有点不可思议。

让他不可思议的事情还在后头。

我想起了自己多年前,刚接触 JavaScript 时,学习到的一些知识点。

比如 JavaScript 里,0.1 + 0.2 = 0.30000000000000004.

我儿子第一次看到 JavaScript 这个计算结果时,也很吃惊。

十进制小数 0.1 转二进制的计算过程:

  • 0.1*2=0.2……0——整数部分为 0 。整数部分 0 清零后为 0,用 0.2 接着计算。
  • 0.2*2=0.4……0——整数部分为 0 。整数部分 0 清零后为 0,用 0.4 接着计算。
  • 0.4*2=0.8……0——整数部分为 0 。整数部分 0 清零后为 0,用 0.8 接着计算。
  • 0.8*2=1.6……1——整数部分为 1 。整数部分 1 清零后为 0,用 0.6 接着计算。
  • 0.6*2=1.2……1——整数部分为 1 。整数部分 1 清零后为 0,用 0.2 接着计算。
  • 0.2*2=0.4……0——整数部分为 0。 整数部分 0 清零后为 0,用 0.4 接着计算。
  • 0.4*2=0.8……0——整数部分为 0 。整数部分 0 清零后为 0,用 0.8 接着计算。
  • 0.8*2=1.6……1——整数部分为 1。 整数部分 1 清零后为 0,用 0.6 接着计算。
  • 0.6*2=1.2……1——整数部分为 1。 整数部分 1 清零后为 0,用 0.2 接着计算。
  • 0.2*2=0.4……0——整数部分为 0。 整数部分 0 清零后为 0,用 0.4 接着计算。
  • 0.4*2=0.8……0——整数部分为 0。 整数部分 0 清零后为 0,用 0.8 接着计算。
  • 0.8*2=1.6……1——整数部分为 1。 整数部分 1 清零后为 0,用 0.6 接着计算。
    ... 无限循环下去

最后十进制小数 0.1 的二进制表示是 0.000110011001100(无限循环下去)

同理,十进制小数 0.2 的二进制表示是 0.00110011001100110011(无限循环下去)

导致 JavaScript 里 0.1 + 0.2 = 0.30000000000000004 问题的根源,在于二进制系统无法精确表示一些十进制小数,例如 0.1 和 0.2.

在 JavaScript 和许多其他编程语言中,浮点数表示采用的是 IEEE 754 标准,这种标准下最高的 1 位是符号位 S,接着的 11 位是指数 E,剩下的 52 位为有效数字 M.

这种规范试图用有限的 64 位,去描述一个在二进制表示形式下会无限循环的十进制数,超出有效数字位之外的循环节,会进行舍入(round)操作,因此会导致精度损失。

当 0.1 和 0.2 这两个浮点数相加时,二者各自进行舍入操作导致的精度损失,在相加时会累积,导致最终的结果略微偏离我们预期的 0.3.

再试试 ABAP:

结果和 JavaScript 一致:

所以 ABAP 里 0.3 / 0.1 = 2.9999999999999996 也就不难理解了。

看到这里,你认为只有小数才会遇到这些麻烦?

再来看看这个例子:9999999999999999 = 10000000000000000

编码时,变量 v1 的值还是硬编码的 9999999999999999.

运行这个 ABAP 报表,调试器里一看,9999999999999999 变成了 10000000000000000:

再看这个泥牛入海的 1:10000000000000000 + 1 = 10000000000000000

无论是在循环体外,还是循环体内,如果每次只给 10000000000000000 累加一个 1,这个 1 就像肉包子打狗一样,有去无回。

上面代码里两个 WRITE 语句,打印的仍然是 10000000000000000 这个数。

但如果是一次性给 10000000000000000 加上 2,这个累加的 2 就生效了:

说到底,这些看似怪异的结果,还是因为 IEEE 754 二进制浮点运算标准造成的。

9999999999999999 被舍入成了离它最近的一个偶数 10000000000000000. 而 10000000000000000 加上 1 之后的结果,因为存储精度损失,被舍入回 10000000000000000,因此无论是直接加1,还是在循环里逐次累加,最后的舍入结果仍然为 10000000000000000.

如果对这些例子感兴趣想深入研究,建议阅读 SAP ABAP 帮助文档:Binary Floating Point Numbers 一节。

本来我儿子觉得计算机内部的计算是绝对"精确",永远不会"出错",远远胜过人类的,但是看了这些例子,他对计算机计算能力的信仰崩塌了,囧。

回到本文开头的例子,50% + 50% = 0.75,知乎上被大多数朋友接受的一种说法:这是一种 working as designed 的行为:

相关文章

  • 2018-12-29

    今天早上儿子补写作业写到7:50 ,然后叫我过去,我过去一看7:50啊,我说儿子不早啦,已7:50 了。儿子原来平...

  • 50指数基金怎么选

    说起50指数,大家应该都会想起上证50、基本面50、50AH、央视50这四只指数,那么这四只50指数有什么区别呢?...

  • 棍鞭底下真的出孝子吗?

    今天我问儿子,“如果满分100分,你爱妈妈多少分?”儿子回答:“50分”。“才50分?不会吧?” 我记得以前儿子特...

  • 感赏与投射91~值班

    今天周六儿子起床可以晚10分钟6:50,我开始叫儿子起床,他在里面回应着。7:00后,我在叫孩子,儿子回应说起...

  • 畅想一下自己50岁的时候生活是什么样子?2018-03-01

    距离自己50岁,还有十几年的时间,说起来,好像也不是很远的样子。我50岁的时候,生活是什么样子呢? 我希望我50岁...

  • 我的信仰崩塌了

    “我的信仰崩塌了”,这句话一直在我耳边围绕着。 他们说,你要相信这个世界上会有懂你并且呵护你的人,只是还没有等到。...

  • 50文:信仰观

    教堂门前半跪蹲,不是多情向美人。森严教堂当肃穆,都是人心造化功。壮志坚定可移山,教化众生有神通。心诚则灵!

  • 安神的投资札记——指数跟踪周报(20220930)

    本周关键词:跌一涨二 本周,跟踪的所有规模指数继续下跌,医卫大幅反弹。 上证50,-0.75%; 沪深300,-1...

  • 关于你

    我是集市里的养猫者,不看路人,不换信仰 所以,信仰塌了就是塌了,英雄没了就是没了 你的所有挣扎都于事无补

  • 糟糕,是心动的感觉

    【每日精进】50/50 我是邓琴,今天是你陪我的第50天,满满的感恩。 01.不知不觉,从国庆到现在50天就这样到...

网友评论

      本文标题:从 50% + 50% = 0.75 说起:我儿子的信仰崩塌了

      本文链接:https://www.haomeiwen.com/subject/axanndtx.html