10% + 10% 他为什么等于0.11
好久没有更新了,最近比较忙。昨天微博上突然「曝光」(现在这个词都只有读bào光了,高考的时候还是pù光,歪题了)出好多安卓手机的简单计算器在计算10%+10%时居然给出了结果是0.11,不是人们预想的0.2=20%。
现象
先描述现象,简单计算器中,计算某项加或减百分数。xx + n% / xx - n%
1 | BNF表示: |
此时计算器会将算式解释为 xx 为某个计算结果,+/-n%代表着对当前计算结果进行+/-n%倍的计算。例如昨天产生的争议10% + 10%,被解释为10% + 10% * 10%。事实上前边的表达式为纯数字/计算结果时,也会有同样的现象。例如6 + 10%,计算结果为6 + 6 *10% = 6.6。这种现象其实在Windows计算器的标准模式下也会出现,还有一干的安卓计算器的简单模式。
结论
再说结论,先把结论放上来,有兴趣的可以继续看,不想继续了解的看完结论就明白了。首先,结论是,这不是bug,这是产品特性。因为在这些计算器里就是这样定义百分数计算的。这些计算器将+/-百分数的计算解释为对之前的结果进行加/减比例的计算。
之所以这样做,是因为在设计计算器的算法时,出于一个前提,即人们很少计算百分数的直接加减法,更常见的是诸如在计算税收,利息等等问题时,会对某个结果进行(1 + n%)的计算。比如存款10000元,年利息5%,一年后的存款为10000 + 5%,得到10500。计算器一般都是边解释边进行计算的,所以实时都会出现一个当前结果。也可能早起的计算器屏幕有限,输入方法不够友好,所以不方便输入1 * (1 + 10%)这种结合,所以有这种设计。
而针对乘法/除法,用户的需求就是直接对某个数计算百分比倍数,所以和我们通常认知的结果是一致的。
所以这种现象只是产品设计,不是bug,windows和安卓都存在。安卓软件可能用了相同的开源组件/库或者内置计算器等,而部分手机没有采用这种解决方案。至于看到一些乱分析说自己是学计算机的,这种明显就是浮点数的问题blabla的,笑一笑就ok了。结论很简单,是审计问题,不是bug,是「产品经理」的锅hhh。
分析
我最开始也以为是语义分析一类做错了导致的bug,后来一想,计算器一般都采用的是逆波兰式进行分析计算的,应该不存在二义性,而且简单计算器都是一边计算一边出结果的,显然不会有这样的bug。后来发现微软在Windows中的计算器也是这样设计的,那应该是某种设计问题。刚好微软的计算器开源了,那我们找一下他们是怎样实现的。
Windows计算器中的标准模式中对于百分数计算的定义:
1 | case IDC_PERCENT: |
这样逻辑就十分清晰了,注释中也解释了会将加减百分数计算解释为result = pre-result * persent / 100(1 + 10%解释为 1 + 1 * 10 / 100)
至于逆波兰式,有兴趣的话我可以再写一篇解释一下解释过程和计算过程。
参考:
Microsoft github caculator
row 79