先看一个事:在大多数计算器上敲 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/41/3 + 1/6 = 1/2√8 = 2√2√2 + √3 = √2 + √3 (就保持这个形式)√(3+2√2) = 1 + √2 (嵌套根式自动拆开)1/√3 = √3/3sin(π/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(底数)*指数只要位数。
计算器现在放出来了,在下载区,免费。