返回
1072 字
5 分钟
0 次阅读
0.1 + 0.2 为什么算不对,我是怎么绕过去的

先看一个事:在大多数计算器上敲 0.1 + 0.2,你会得到 0.30000000000000004。小学生都知道这是错的,但计算器就是这么答。

原因不复杂:电脑存小数用的是二进制浮点数,而 0.1 在二进制里是个无限循环小数, 存不进去,只能存一个很近似的值。两个近似值一加,误差就露出来了。 这不是计算器的 bug,是它背后那套东西的天花板。

我做「全能计算器」就是为了掀掉这个天花板。

不用第三方库,一层一层往上搭#

整个计算器是纯 Python 标准库写的,一个外部依赖都没有。核心是一个”精确数塔”, 数值按精确度分成五层,任何运算都优先在最高那一层完成:

  • int:任意精度整数,位数没有上限,Python 原生就支持。
  • Fraction:分数。加减乘除永远是精确的,自动约分。
  • Rad:形如 Σqᵢ√rᵢ 的代数数。√2、√2+√3、√6/3 都是精确的符号,不是小数。
  • PiMul:形如 a + bπ,π/6、2π/3 这种也保持精确。
  • Decimal:真正超越的情形才用它,比如 sin、ln、e^x。

所以它会这么算:

0.1 + 0.2 = 3/10 (不是 0.30000000000000004)
0.75 = 3/4
1/3 + 1/6 = 1/2
√8 = 2√2
√2 + √3 = √2 + √3 (就保持这个形式)
√(3+2√2) = 1 + √2 (嵌套根式自动拆开)
1/√3 = √3/3
sin(π/6) = 1/2 (直接给精确值,不是 0.49999999999999994)

最后那条我挺得意。三角函数的精确值表我是单独做的,而且能认出”这个输入在数值上等于 π 的 有理数倍”,然后按精确值返回。

π 是怎么算到两万位的#

整数和分数不难,难的是 π 这种无理数。我用的是 Chudnovsky 公式加二叉分裂, 全程整数运算,两万位大概 0.5 秒。

e、ln、exp、sqrt 走的是 libmpdec 的任意精度实现; sin、cos、tan、Γ 这一批是我自己写的级数和渐近展开。

界面也是自己画的#

这部分花的时间比算法还多。我的界面没有用现成的控件去拼,是自己用 tkinter 一块一块画的, 光界面那块代码就两千多行。

最麻烦的是二维公式排版。分数不能写成 1/3 糊弄过去, 得是分子在横线上、分母在横线下;根号要有真的钩和上横线;乘方要把指数抬高缩小。 根号套分数、分数套根号也得能排。内容太宽太高的时候,整块自动缩放到放得下。

输入 1/3+1/6,你看到的是这样:

1 1 1
─ + ─ = ─
3 6 2

“无限”也是有边界的,我写在明面上#

我不想说”支持无限位”这种话。真实情况是这样:计算器本身不设位数上限, 但有些规模物理上就是做不到,这时候它会直接告诉你为什么,而不是闷头硬算。

  • 99999^99999 → 499,995 位,一秒左右。
  • 99999^999999 → 499 万位,大概 11 秒,会另开一个进程算,带进度,随时能停。
  • (99999^99999)^99999 → 大约 4.99×10¹⁰ 位,光把这些数字装进内存就要 20 GB,直接拒绝,并说明原因。

sqrt(-1) 它也不会给你编一个 i 出来,而是提示不支持复数。 极少数大到分解不动的根式,会退化成高精度小数,但界面上会老老实实标一个 ≈, 不假装那是精确值。

报错也要说人话#

写这个的时候我一直在想一件事:我数学也不算好,如果我输入错了,我希望它骂我一句 还是告诉我错在哪?

所以它是后面那种:

  • 敲 (1+2 → 会说”第 1 个字符的这个「(」没有配对的「)」,数一数是不是少写了一个”,同时在输入框里直接选中那个字符。
  • 敲 2 3 → 会说”第 3 个字符这里有问题,「3」前面少了运算符,是漏写了 + - × ÷ 吗”。
  • 结果大得算不出来 → 会告诉你大约多少位、要多少内存,再给三条可行的建议,比如改用 log10(底数)*指数 只要位数。

计算器现在放出来了,在下载区,免费。

这篇文章讲的东西,可以在这里下载

全部东西都在下载区, 包括模组、地图和软件。

0.1 + 0.2 为什么算不对,我是怎么绕过去的
https://liyuanjie.com/posts/seventh-post/
作者
Liyuanjie
发布于
2026-10-02
许可协议
CC BY-NC-SA 4.0
这篇被看了 0 次