超长数字计算丢失精度问题的详细解决过程
创作者俱乐部成员
首先,详细描述一下浮点数造成的问题:
如图,VBA、JSA、Python、Powershell里,都认为0.1 + 0.2不等于0.3,是不是非常恶心,常见语言里用浮点计算必须十分小心
在wps的公式里计算超过15位的数字时,会变成科学计数法,如果改变单元格格式,可以发现数字末尾全变成了0,数字丢失了精度
还有就是前几天吐槽过的round公式的奇怪行为
然后,常见的解决方法就是把数字当成字符串来处理,比如,自己写个加法,按照十进制逐个字符相加,进位,最后生成相加的字符串,这样很好,但需要写很多代码。
所以我们去找现成的库,比如,npm里的js库big.js既很小,又可以在JS宏里直接使用;又比如busybox-w32里的bc命令,无限精度计算,没有浮点误差。两者都是很好的选择
如何使用big.js
去github上下载big.js(不能发链接),用记事本打开,全选复制内容
打开wps宏编辑器,点击工具,选项,编译,起码要关掉图中第一项,"禁止全局表达式"
然后把big.js粘贴到当前模块,再新建一个模块,根据big.js的说明书,写一个自定义公式
🔔 | function sum7(x) { let n = new Big(x) return n.plus(7).toFixed(0) } |
意思就是:从单元格传递过来的长数字字符串是x
用new Big(x)生成一个类似逐个字符组成的十进制数字对象n
然后这个n有plus、times等计算方法,可以调用这些方法获取计算结果
这个对象的计算可以有任意精度
如何使用bc命令
去github下载busybox.exe,可以把这个exe放到C:\Windows目录里,也可以放到环境变量PATH里任何一个目录里,目的是在命令行任何地方都可以调用这个exe
然后在当前xlsm文件的相同目录下建立cgi-bin文件夹,目的是在这个目录启动httpd服务,脚本都写在cgi-bin里,其实可以放到任何其他目录,只是后面需要相应修改
在cgi-bin里新建文本文件bc.sh,内容如下,前两行声明这是Shell脚本,内容是utf8编码,第三行是构造一句加法,传递给bc计算,返回结果
📌 | #!/bin/sh echo -e 'Content-type: text/plain; charset=utf-8\n' sed 's/.*/& + 7/' | bc -l |
最后在JS宏里定义宏,内如如下,第一句先尝试关掉已有的httpd,然后启动httpd服务,第二句fetch发送post请求,内容是A1单元格的内容,把接收到的计算结果打印到立即窗口
👋 | unction test() { Shell(`cmd /c busybox pkill httpd || busybox httpd -p 8080 -h "${ThisWorkbook.Path}"`, jsHide) fetch(`http://localhost:8080/cgi-bin/bc.sh`,{method: "POST", body: Range("A1").Value2}) .then(r=>r.text()).then(console.log) } |
😁还好,两种方式写的代码都不多
说点题外话,为什么要用命令:
总的来说,因为有读写外部文件的需求
VBA里可以通过com对象,访问外部文件、数据库、程序
JS里现有的访问外部的方法里:
open、put、lineinput命令,get目前没法用,lineinput有每行长度限制,最后一行都不进来,总之就是非常不稳定
nativex文档太少,c++太难,linux下说是可以编译成so文件,但没见到任何例子
xll加载项,不能用混合模式的库,卸载时报错,linux好像不能用
wpsjs加载项,OAAssist不能用了,但是可以通过上面提到的httpd,开启cgi服务,读写外部文件,支持linux
fetch,同样需要开启cgi服务,支持linux
所以,可以看出linux专业版支持的JS宏里可以用fetch,linux个人版可以用wpsjs加载项,win版里其他方法都不太稳定fetch最稳定,wpsjs方便分享,但总的来说,想访问外部数据,都需要cgi服务,而busybox-w32这个exe只要600K大小,其中的httpd命令提供了一个基本的cgi服务,busybox里剩下的100多个命令提供了大量读写外部数据的方法,如上面所示,脚本基本就是3行代码。
总的来说,用这个方式给JS宏提供对外操作数据的接口,可以享受fetch的成熟稳定,也只需要付出600K的一个文件,和3行的脚本即可
当然,如果你熟悉任何语言,都可以简单的开启cgi服务,这不是唯一也不是最合适的选择,只是很容易上手进行尝试
前后呼应一下,bc里认为0.1+0.2等于0.3,哈哈
创作者俱乐部成员