news 2026/10/15 22:23:04

bignumber.js 性能与正确性对比指南:用 perf 目录的 bigtime 工具交叉验证 BigDecimal 移植实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
bignumber.js 性能与正确性对比指南:用 perf 目录的 bigtime 工具交叉验证 BigDecimal 移植实现
  • 后端

【免费下载链接】bignumber.js

A JavaScript library for arbitrary-precision decimal and non-decimal arithmetic

项目地址:https://gitcode.com/gh_mirrors/bi/bignumber.js
点击查看免费下载

本指南完整讲解 bignumber.js 仓库perf目录下三个遗留(legacy)但依然可用的对比测试工具:命令行程序bigtime.js与bigtime-OOM.js,以及浏览器页面bignumber-vs-bigdecimal.html。它们能在大量随机输入上同时运行 bignumber.js 的方法与两个 JavaScript 版 BigDecimal(GWT 移植与 ICU4J 移植)的对应方法,逐一核对结果是否一致并统计耗时,适合做正确性交叉验证与性能基准。读完本文,你将掌握这三个工具的参数语义、默认值、底层执行流程与结果校验原理,并能用它们复现 GWT BigDecimal 移植中的已知缺陷。

perf 目录的定位:旧工具、新用途

perf目录(见 perf/README.md)中存放的是项目作者早期编写的一批对比测试程序。它们虽然年代较早,但在当前仓库中依然可以运行,其价值在于:在大量随机输入上交叉验证 bignumber.js 的计算结果。这也是仓库主 README.md 的 "Test" 一节中明确提到的用法——"还有一些位于perf的旧程序仍然可用,可用于在大量随机输入上测试与交叉核对结果"。

目录结构如下:

  • perf/bigtime.js —— Node.js 命令行对比计时工具(推荐使用);
  • perf/bigtime-OOM.js —— 同样用途,但额外输出对象创建与方法调用的分段时间,代价是有内存溢出风险;
  • perf/bignumber-vs-bigdecimal.html —— 浏览器中的可视化对比页面;
  • perf/lib/bigdecimal_GWT/ —— GWT 移植的java.math.BigDecimal;
  • perf/lib/bigdecimal_ICU4J/ —— ICU4J 移植的com.ibm.icu.math.BigDecimal。

两个命令行工具通过require('..')直接加载仓库根目录的 bignumber.js 入口(perf/bigtime.js),再require各自lib目录下的 BigDecimal 移植版本,因此无需安装任何 npm 依赖即可运行(唯一前提是仓库根目录的bignumber.js文件存在,它本身是单文件源码)。

bigtime.js:命令行对比计时的首选

基本用法

在仓库根目录执行:

node perf/bigtime plus 10000 40

这条命令的含义是:对 bignumber.js 的plus方法与 GWT BigDecimal 的add方法各计时 10000 次调用,操作数使用最多 40 位的随机数字,并逐一检查两边结果是否一致。执行结束后会输出如下信息:方法名、调用次数、随机操作数个数、最大位数、BigDecimal 与 BigNumber 各自的总耗时,以及"谁快多少倍"的结论(若两边耗时差不超过 2 毫秒则显示 "Times approximately equal")。

查看完整帮助:

node perf/bigtime -h

帮助文本由脚本自身在参数无法识别时打印(见 perf/bigtime.js),其中给出了完整参数语法与默认值说明。

参数语义与默认值

命令行语法为:

node bigtime [METHOD] [METHOD CALLS [MAX DIGITS [DECIMAL PLACES]]]

各位置参数(从 perf/bigtime.js 的参数解析逻辑可确认):

位置参数说明默认值
1METHOD要计时并对比的方法名(见下方映射表)随机选择
2METHOD CALLS每个实现的方法调用次数10000(pow为 100)
3MAX DIGITS随机操作数的最大位数20(pow为 20)
4DECIMAL PLACES除法使用的小数位数20

对应的常量定义在 perf/bigtime.js:DEFAULT_REPS = 1e4、DEFAULT_POW_REPS = 1e2、DEFAULT_MAX_DIGITS = 20、DEFAULT_POW_MAX_DIGITS = 20、DEFAULT_PLACES = 20。脚本还会校验数值上限:调用次数超过1e10、位数超过1e6时按非法值处理。

帮助中给出的典型示例包括:

node bigtime # 完全随机 node bigtime minus # 指定方法 node bigtime add 100000 # 10 万次调用 node bigtime times 20000 100 node bigtime div 100000 50 20 node bigtime 9000 # 只给次数 node bigtime 1000000 20 node bigtime m add # 附加内存统计

附带参数m或-m可开启内存变化统计(rss、heapUsed、heapTotal,单位 KB)。

方法映射表

脚本内部维护了三张方法名数组(perf/bigtime.js),将 BigDecimal 方法与 bignumber.js 方法一一对应:

BigDecimal(GWT 移植)bignumber.js说明
addplus加法
subtractminus减法
multiplymultipliedBy(别名times)乘法
dividedividedBy(别名div)除法
remaindermodulo(别名mod)取模
compareTocomparedTo比较
powexponentiatedBy(别名pow)幂
negatenegated取负
absabsoluteValue绝对值

命令行参数 METHOD 优先从 bignumber.js 一侧识别(plus、minus、dividedBy、div、mod等),脚本会自动匹配对应的 BigDecimal 方法名(perf/bigtime.js)。注意:bigtime.js同时支持一元方法(abs、negate,只需一个操作数),这是它与bigtime-OOM.js的一个重要差异。

执行流程:分批生成、分批计时、逐批校验

从源码结构看,bigtime.js的核心执行流程(perf/bigtime.js)分为四步:

  1. 分批处理:外层循环for (; reps > 0; reps -= 1e4)将总调用次数切分为每批最多 1 万个(j = Math.min(reps, 1e4))。这是它能"不容易耗尽内存"的根本原因——随机数数组与结果数组每批只保留 1 万个元素,随用随弃。
  2. 生成随机操作数:getRandom(maxDigits)(perf/bigtime.js)生成普通记法的随机数字字符串——位数在 1 到maxDigits之间随机,约 20% 概率为整数,50% 概率为负数;若目标是divide/remainder且随机到 0,会强制替换为非零数字以避免除零错误。pow的第二个操作数是 0~50 的随机整数指数(MAX_POWER = 50)。README 中记载该工具以指数记法生成随机数,不过当前源码中启用的生成器为普通记法,指数记法版本以注释形式保留在 perf/bigtime.js。
  3. 分别计时:先对 BigDecimal 侧计时(bdT),再对 BigNumber 侧计时(bnT),每批累加到bdTotal/bnTotal;若开启了m参数,每段前后调用getMemory统计内存增量(perf/bigtime.js)。
  4. 逐结果校验:将bnRs[i].toString()与bdRs[i].toPlainString()做字符串比较;BigDecimal 的非整数结果会先用正则/\.?0+$/去掉尾部零再比较(perf/bigtime.js)。一旦发现首个不一致,立即打印双方结果与操作数(除法场景还会附上dp与rounding)并终止。

全部通过则汇总耗时,调用getFastest(perf/bigtime.js)给出结论:只有当两边总耗时之差大于 2 毫秒时才判定快慢,并输出倍数(保留 1 位小数);否则输出 "Times approximately equal"。

脚本内预设的 BigNumber 配置

两个命令行工具在执行前都会通过BigNumber.config设置一组对比友好的配置(perf/bigtime.js):

BigNumber.config({ EXPONENTIAL_AT: 1E9, // 超过该阈值才使用指数记法输出,避免结果被科学计数法干扰比对 RANGE: 1E9, // 扩大可表示数值的指数范围 ERRORS: false, // 早期版本配置项(控制错误抛出),属于历史遗留设置 MODULO_MODE: 1, // 等于 BigNumber.EUCLID,使用欧几里得模 POW_PRECISION: 10000 // pow 结果的小数位数上限 });

其中EXPONENTIAL_AT: 1e9、RANGE: 1e9与 doc/API.html 中记载的默认值EXPONENTIAL_AT: [-10, 20]、RANGE: 1e+9(见 doc/API.html)不同,是为了让所有操作数以普通十进制字符串输出、便于与 BigDecimal 结果比对。此外,当被测方法为divide时,脚本会随机选取一个舍入模式(Math.floor(Math.random() * 7),对应 0~6)并同步设置DECIMAL_PLACES与ROUNDING_MODE(perf/bigtime.js),使两边在相同舍入条件下对齐结果。

bigtime-OOM.js:分项计时版,但小心内存

bigtime-OOM.js与bigtime.js参数语法一致(node perf/bigtime-OOM [METHOD] [METHOD CALLS [MAX DIGITS [DECIMAL PLACES]]]),差异在于它把"对象创建"与"方法调用"分开计时,并额外计算操作数的平均位数。

与 bigtime.js 的完整对比

perf/README.md 的 "Further notes" 部分给出了两份工具的详细差异,整理如下:

维度bigtime.jsbigtime-OOM.js
随机数与对象的创建方式分批(每批最多 1 万个)创建一次性全部创建
内存风险基本不会耗尽内存可能内存溢出,例如iterations > 500000且random digits > 40时
计时粒度不分对象创建与方法调用分别输出对象创建耗时与方法调用耗时
被测方法一元与二元方法均覆盖(含abs、negate)仅二元方法(无abs、negate)
随机数生成进度不提示完成情况打印"Creating N random numbers... done"
平均位数统计无计算并打印操作数平均位数
随机数记法README 记载为指数记法(当前源码为普通记法)普通记法

执行流程与输出格式

bigtime-OOM.js的流程(perf/bigtime-OOM.js)是:先生成全部随机数 → 计时创建 BigDecimals → 计时创建 BigNumbers → 计时双方方法调用 → 输出分项结论 → 统一校验结果。

输出示例结构:

BigNumber plus vs BigDecimal add Method calls: 10000 Creating 20000 random numbers (max. digits: 20)... done Average number of digits: 10 Creating BigDecimals... done. Time taken: 123 ms Creating BigNumbers... done. Time taken: 45 ms Object creation: BigNumber was 2.7 times faster BigDecimal add... done. Time taken: 456 ms BigNumber plus... done. Time taken: 89 ms Method calls: BigNumber was 5.1 times faster Overall: BigNumber was 4.8 times faster Checking for mismatches... done. None found.

内存统计通过process.memoryUsage()的rss、heapUsed、heapTotal差值计算(perf/bigtime-OOM.js),同样需要m参数开启。

内存溢出的触发条件

README 明确指出:当iterations > 500000且random digits > 40时,bigtime-OOM.js可能耗尽内存。原因是它将reps个操作数与结果全部保存在数组中(perf/bigtime-OOM.js),数字越大、位数越长,占用内存越多。相比之下bigtime.js采用reps -= 1e4的分批策略(perf/bigtime.js),因此被 README 推荐为常规选择。

浏览器版本:bignumber-vs-bigdecimal.html

在浏览器中打开 perf/bignumber-vs-bigdecimal.html 即可使用可视化对比页面。页面加载了两个脚本(perf/bignumber-vs-bigdecimal.html):

<script src='../dist/bignumber.js'></script> <script src='./lib/bigdecimal_GWT/bigdecimal.js'></script>

注意:它引用的是构建产物dist/bignumber.js,因此使用前需要先在仓库根目录执行npm run build(或node build.js)生成dist目录,详见 README.md 的 Build 一节。ICU4J 版本则是页面运行时通过动态插入<script>按需加载(perf/bignumber-vs-bigdecimal.html)。

界面操作说明

页面整体"自解释"(README 原话),核心控件包括:

  • 方法单选按钮(perf/bignumber-vs-bigdecimal.html):plus/minus/multipliedBy/dividedBy/modulo/comparedTo/absoluteValue/negated/exponentiatedBy,对应 BigDecimal 的add/subtract/multiply/divide/remainder/compareTo/abs/negate/pow;
  • BigDecimal 版本:GWT 或 ICU4J 单选;当 GWT 且勾选 "Integers only" 时,右侧标注会切换为BigInteger(此时使用 GWT 移植一并导出的java.math.BigInteger);
  • 随机数位数:Random number digits输入框,配合Fixed(固定位数)/Max(最大位数)单选,以及 "Integers only" 复选框;
  • 除法专属选项:选择dividedBy时出现Decimal places输入框与Rounding下拉(UP / DOWN / CEIL / FLOOR / HALF_UP / HALF_DOWN / HALF_EVEN,对应舍入模式 0~6);
  • Iterations:迭代次数输入框;勾选 "Show all (no timing)" 则关闭计时、逐条展示每次运算的输入与输出;
  • Start 按钮:开始/重启测试;再次点击任意方法单选按钮可立即停止;按空格键暂停/继续(事件绑定见 perf/bignumber-vs-bigdecimal.html)。

页面默认值(perf/bignumber-vs-bigdecimal.html):迭代 10000 次、随机数最多 20 位、除法小数位 20、舍入模式 4(HALF_UP)、默认选中 GWT 与 Max。页面同样预先配置了DECIMAL_PLACES: 20、ROUNDING_MODE: 4、EXPONENTIAL_AT: 1e9、RANGE: 1e9、MODULO_MODE: 1、POW_PRECISION: 10000(perf/bignumber-vs-bigdecimal.html)。

两个移植版本的 API 差异(页面源码注释)

页面末尾的源码注释(perf/bignumber-vs-bigdecimal.html)记录了开发者实测得出的两版差异:

  • ICU4J(IBMcom.ibm.icu.math包):pow的参数必须是 BigDecimal 类型;没有toPlainString()与stripTrailingZeros();只导出BigDecimal。页面注释中的经验记录是:在 Firefox 上远快于 GWT 版,在 Chrome 上则随方法而异;
  • GWT(Java 标准类库java.math.BigDecimal):导出RoundingMode、MathContext、BigDecimal、BigInteger;pow的参数必须是 number 类型;提供toPlainString、stripTrailingZeros等方法。

正因如此,页面在比对结果时对两种版本采用不同的规范化策略(perf/bignumber-vs-bigdecimal.html):GWT 版用stripTrailingZeros().toPlainString(),ICU4J 版用toString()后正则剔除尾部零。命令行工具则统一使用toPlainString()加正则处理(GWT 版提供该方法)。

BigInteger 模式的限制

勾选 "Integers only" 且选择 GWT 时,对比对象切换为BigInteger,此时页面会自动禁用dividedBy单选按钮并切回plus(perf/bignumber-vs-bigdecimal.html)。原因在注释中说明:BigInteger在除法"无法得到精确可表示的小数结果"时会抛出异常,因此无法参与除法对比。

交叉验证的价值:GWT BigDecimal 移植的已知缺陷

README 特别提醒:npm registry 中的 BigDecimal 包就是 GWT 版本,它存在若干缺陷。具体缺陷示例集中写在 perf/lib/bigdecimal_GWT/bugs.js 中(运行方式为node perf/lib/bigdecimal_GWT/bugs),涉及remainder、divide、compareTo三个方法:

方法输入GWT 实际输出正确结果
remainder9.785496E-2对-5.9219189762E-20.097854960.038635770238
remainder1.23693014661017964112E-5对-6.9318042E-70.00001236930146610179641120.0000005852343261017964112
divide(40 位,ROUND_HALF_EVEN)6.9609119610E-78 ÷ 4E-481.7402279903E-301.7402279902E-30
divide(40 位,ROUND_HALF_EVEN)5.383458817E-83 ÷ 8E-546.7293235213E-306.7293235212E-30
compareTo0.04与0.0793930681-1
compareTo7.88749578569876987785987658649E-10与4.2545098709E-61-1

这些"正确结果"的权威来源是同一目录下的 Java 程序 perf/lib/bigdecimal_GWT/BigDecTest.java(用javac BigDecTest.java && java BigDecTest编译运行,输出即注释中的预期值)。它使用的正是 Java 标准库java.math.BigDecimal,可作为 GWT JavaScript 移植版的参照基准——这也解释了为什么交叉验证需要同时准备 GWT 与 ICU4J 两个移植版本:它们各自独立移植自不同来源,彼此结果相互印证,任何一方与 bignumber.js 结果不一致都值得深究。

结果一致性校验的原理

三个工具共享同一套结果比对思想:BigDecimal 结果规范化后与 BigNumber 的toString()逐字符比较。规范化规则(见 perf/bigtime.js):

  1. 取 BigDecimal 的toPlainString()(ICU4J 版退化为toString());
  2. 若含小数点,用正则\.?0+$剥掉尾部零(例如"0.500"→"0.5");
  3. 与bnRs[i].toString()严格相等才视为通过。

任何一侧结果不一致都会打印首个失配样本的完整上下文——操作数、方法、结果、除法参数(dp、rounding)——方便直接复现。这正是这套工具作为"大规模随机输入的交叉核对"手段的核心价值:普通单元测试覆盖不到的边界组合,可以通过海量随机样本暴露出来。

使用建议与注意事项

  • 优先使用bigtime.js:README 明确推荐它,原因是内存占用可控(分批执行)、覆盖一元与二元方法;bigtime-OOM.js仅在需要区分"对象创建"与"方法调用"耗时、且输入规模可控(避开iterations > 500000且digits > 40的组合)时使用。
  • 浏览器页面需先构建:它引用dist/bignumber.js,请先执行npm run build再打开 perf/bignumber-vs-bigdecimal.html。
  • 对比前先确认配置对齐:除法对比时舍入模式由脚本随机选取(0~6),DECIMAL_PLACES通过命令行第 4 个参数指定;脚本内部已将EXPONENTIAL_AT、RANGE、MODULO_MODE、POW_PRECISION预设为对比友好值(详见 doc/API.html 中各配置项的定义)。
  • 结果不一致不等于 bignumber.js 出错:如上一节所示,GWT 移植本身存在remainder、divide、compareTo缺陷,遇到失配时应先对照 perf/lib/bigdecimal_GWT/BigDecTest.java 与 ICU4J 版本确认基准的正确性。
  • 后端

【免费下载链接】bignumber.js

A JavaScript library for arbitrary-precision decimal and non-decimal arithmetic

项目地址:https://gitcode.com/gh_mirrors/bi/bignumber.js
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 22:20:36

Coze智能体搭建实战:从零到一交付业务级Bot

1. 这不是又一个“三分钟上手”的幻觉&#xff0c;而是真正能让你在三天后独立交付智能体的实操路径你点开这个标题时&#xff0c;大概率正被两类信息包围&#xff1a;一类是铺天盖地的“Coze五分钟搭建AI助手”短视频&#xff0c;画面炫酷、流程丝滑&#xff0c;但你照着操作到…

作者头像 李华
网站建设 2026/10/15 22:20:17

用C#和Open XML SDK为Excel文件添加内置与自定义文档属性

大家可能都觉得&#xff0c;给 Excel 写代码就是往单元格里塞数据&#xff0c;真正做过企业文件交付的人才会意识到&#xff0c;文件信息栏里的标题、作者、关键词、备注这些属性&#xff0c;比单元格内容还容易被人忽视&#xff0c;却又特别要命。我在自动化出单、报告归档、审…

作者头像 李华
网站建设 2026/10/15 22:19:55

多目标进化算法NSGA-II在综合能源优化调度中的应用与Matlab实现

先说一个我自己的体会&#xff1a;刚接触综合能源优化调度那会儿&#xff0c;我一度被"运行成本最小"和"碳排放最小"两个目标折腾得够呛。用加权法把多目标压成单目标&#xff0c;难的不是求解&#xff0c;而是权重怎么定都有人质疑&#xff1b;今天换一组…

作者头像 李华
网站建设 2026/10/15 22:19:00

AI改代码越改越乱?最小可复现示例让它一击命中

我大半夜盯着终端里那条排在最上面的报错&#xff0c;脑子里只有一个念头&#xff1a;完了&#xff0c;这代码被 AI 改乱了。项目原本 200 行出头&#xff0c;三轮修改之后变成了 500 行&#xff0c;报错倒是没变多——它换着花样变。从 NoneType 的属性错误变成字符串的属性…

作者头像 李华
网站建设 2026/10/15 22:18:51

开源轻量级看板工具vibe-kanban:小团队自托管项目管理实战指南

1. 为什么我会盯上 vibe-kanban&#xff1a;从“用不起”到“自建一个”做项目管理这些年&#xff0c;工具换了一茬又一茬。最早用Excel排期&#xff0c;后来用在线白板&#xff0c;再往后跟着团队用了一堆商业化看板工具&#xff0c;功能倒是越来越全&#xff0c;但价格也越来…

作者头像 李华
网站建设 2026/10/15 22:18:30

OpenCode深度使用完全指南:从零配置到高效开发

1. 从零开始&#xff1a;为什么我决定深度使用OpenCode先说说背景。我在日常开发中负责好几个跨平台项目的维护&#xff0c;经常需要在不同的开发环境之间切换。以前主力编辑器一直用的是重量级IDE&#xff0c;功能倒是全&#xff0c;但每次启动都要等上十几秒&#xff0c;插件…

作者头像 李华