资讯动态

Effect BigDecimal 聚合运算新能力:sumAll 与 multiplyAll 源码解析与实战

发布时间:2026/9/14 9:26:14 来源:尧图企业网站定制
Effect BigDecimal 聚合运算新能力sumAll 与 multiplyAll 源码解析与实战【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect本文围绕 Effect 中BigDecimal模块新增的sumAll与multiplyAll两个聚合 API 展开介绍其在金额、数量等高精度十进制场景下的使用方式、底层实现原理以及它与Number、BigInt同族 API 的对齐关系。读者阅读后可以掌握如何对一组成语十进制值做批量求和与求积并能理解其空集合语义、零值短路优化等实现细节。变更背景一次与Number、BigInt的功能对齐本仓库的变更记录 .changeset/pre/add-bigdecimal-sumall-multiplyall.md 记录了这样一次版本变更effect包patch级别AddedBigDecimal.sumAllandBigDecimal.multiplyAllfor feature parity withNumberandBigInt, closes #1880.翻译过来即为BigDecimal模块新增sumAll与multiplyAll两个函数目的是与Number、BigInt两个模块保持特性对齐并关闭 issue #1880。在此之前BigDecimal只提供了二元运算sum相加与multiply相乘开发者若要对一个数组或任意可迭代集合中的多个十进制值做累计求和/求积只能手动reduce代码冗长且容易在空集合等边界情况下出错。而Number与BigInt模块早已具备sumAll/multiplyAll本次变更正是补齐了这一缺口。BigDecimal 的内部表示value 与 scale要理解两个新 API 的实现先要了解BigDecimal的数据结构。在 packages/effect/src/BigDecimal.ts 中BigDecimal接口由两部分构成value: bigint无缩放整数unscaled value即去掉小数点后的整数部分scale: number小数位数即小数点后保留的位数。例如字符串123.45会被表示为value 12345n, scale 242则是value 42n, scale 0。这种“bigint scale”的存储方式正是BigDecimal能避免 JavaScriptnumber浮点表示误差如0.1 0.2 ! 0.3的关键所有数字都落在bigint上运算不存在二进制浮点精度丢失。正因如此模块文档packages/effect/src/BigDecimal.ts将BigDecimal定位为“当 JavaScriptnumber的舍入精度不够时使用的十进制数与算术模块”典型场景包括金额money、数量quantities与测量值measurements。sumAll对一组 BigDecimal 批量求和使用方式sumAll接受一个IterableBigDecimal返回这些值的总和单个BigDecimalimport { BigDecimal } from effect const result BigDecimal.sumAll([ BigDecimal.fromStringUnsafe(2), BigDecimal.fromStringUnsafe(3), BigDecimal.fromStringUnsafe(4) ]) // BigDecimal.fromBigInt(9n)它同样适用于任意可迭代对象包括Set、生成器等只要迭代产出的是BigDecimal即可。对于金额累计、订单明细小计合并等场景使用sumAll可以完全绕开 JavaScriptnumber的中间转换始终保持十进制精度。空集合与零值语义sumAll对空集合返回加法的单位元zero即数值0value 0n, scale 0。这一点与数学直觉一致空集之和为零。测试用例 packages/effect/test/BigDecimal.test.ts 明确验证了三种情形assertEquals(BigDecimal.sumAll([]), $(0)) assertEquals(BigDecimal.sumAll([$(2), $(3), $(4)]), $(9)) assertEquals(BigDecimal.sumAll([$(1.5), $(-1.5)]), $(0))其中$是测试文件中的便捷构造函数内部调用fromStringUnsafe。第三个用例说明正负相消时结果同样归零。实现原理sumAll的源码位于 packages/effect/src/BigDecimal.tsexport const sumAll (collection: IterableBigDecimal): BigDecimal { let out: BigDecimal zero for (const n of collection) { out sum(out, n) } return out }其实现非常简洁以zero为累计器初始值遍历集合逐个调用二元sum累加。之所以能保证精度是因为sum在 源码 中会对不同scale的操作数先做scale对齐把较小 scale 的值放大到与较大 scale 一致再对bigint部分做加法最终结果的scale取两者较大者。例如测试中的$(3.00000) $(50) $(53)、$(1.23) $(0.0045678) $(1.2345678)小数位数都得到了完整保留。从实现可以看出一个可以放心使用的特性结果的精度等于参与累加的所有值中最大的 scale中间过程不会因为进位或对齐丢失小数位。multiplyAll对一组 BigDecimal 批量求积使用方式multiplyAll接受一个IterableBigDecimal返回所有值的乘积单个BigDecimalimport { BigDecimal } from effect const result BigDecimal.multiplyAll([ BigDecimal.fromStringUnsafe(2), BigDecimal.fromStringUnsafe(3), BigDecimal.fromStringUnsafe(4) ]) // BigDecimal.fromBigInt(24n)空集合与零值语义与sumAll不同multiplyAll对空集合返回乘法的单位元one即数值1。测试用例 packages/effect/test/BigDecimal.test.ts 验证assertEquals(BigDecimal.multiplyAll([]), $(1)) assertEquals(BigDecimal.multiplyAll([$(2), $(3), $(4)]), $(24)) assertEquals(BigDecimal.multiplyAll([$(2), $(0), $(4)]), $(0))注意sumAll的空集结果是0而multiplyAll的空集结果是1——这是加法与乘法单位元的自然延伸属于易踩坑点务必区分。实现原理与零值短路优化multiplyAll的源码位于 packages/effect/src/BigDecimal.tsexport const multiplyAll (collection: IterableBigDecimal): BigDecimal { let out: BigDecimal one for (const n of collection) { if (n.value bigint0) { return zero } out multiply(out, n) } return out }它的实现比sumAll多了一个值得注意的优化零值短路。一旦遍历中发现某个元素的value 0n立即返回zero不再继续迭代后续元素。这是因为任何数与0相乘结果都是0提前返回既能节省后续乘法开销也保证了正确性测试用例$(2) × $(0) × $(4) $(0)即验证此路径。multiply本身源码的实现为make(self.value * that.value, self.scale that.scale)bigint部分直接相乘scale部分相加。因此multiplyAll结果的 scale 是参与累乘的所有值 scale 之和例如$(3) × $(0.5) $(1.5)scale 从0 1 1。与 Number、BigInt 同族 API 的对齐关系本次变更的动机是“feature parity”特性对齐三个模块的 API 形状完全一致均接受Iterable并返回聚合结果模块求和 API求积 API空集合结果NumberNumber.sumAll(iterable)Number.multiplyAll(iterable)0/1BigIntBigInt.sumAll(iterable)BigInt.multiplyAll(iterable)0n/1nBigDecimalBigDecimal.sumAll(iterable)BigDecimal.multiplyAll(iterable)0/1从源码看Number.sumAllpackages/effect/src/Number.ts与Number.multiplyAllpackages/effect/src/Number.ts的实现与BigDecimal版本如出一辙——multiplyAll同样包含n 0时的提前返回BigInt模块的sumAll/multiplyAllpackages/effect/src/BigInt.ts也保持同样的语义。三个模块的一致性带来一个实际好处如果你的业务代码在Number/BigInt/BigDecimal之间做类型迁移例如先用普通数字原型验证再切换为高精度十进制聚合逻辑几乎可以一字不改地平移只需替换导入模块。这也正是 changelog 中“feature parity”所承诺的开发体验。实战订单金额批量结算结合以上特性看一个贴近真实业务的例子——多行订单明细的金额汇总与折扣计算import { BigDecimal } from effect // 订单行金额单位元全部用字符串构造避免浮点误差 const lineTotals [ BigDecimal.fromStringUnsafe(19.99), BigDecimal.fromStringUnsafe(5.50), BigDecimal.fromStringUnsafe(129.00) ] // 汇总订单金额 154.49精度保留到分 const subtotal BigDecimal.sumAll(lineTotals) // 全场九折 139.041 const discount BigDecimal.fromStringUnsafe(0.9) const total BigDecimal.multiplyAll([subtotal, discount]) console.log(BigDecimal.format(total)) // 139.041其中BigDecimal.formatpackages/effect/src/BigDecimal.ts负责把内部的valuescale表示渲染回人类可读的十进制字符串适合最终展示或落库前的序列化。小结BigDecimal.sumAll与BigDecimal.multiplyAll是 EffectBigDecimal模块为补齐与Number、BigInt特性对齐而新增的一对聚合 API。它们都接受任意IterableBigDecimal并返回单个BigDecimal结果区别在于sumAll以zero为初值逐个累加空集合返回0multiplyAll以one为初值逐个累乘空集合返回1并内置零值短路优化。借助value scale的内部表示两者在整个聚合过程中始终运行在bigint上精度由参与运算的十进制值决定完全规避 JavaScript 浮点误差。需要进一步验证行为细节的读者可以直接阅读对应的测试用例 packages/effect/test/BigDecimal.test.ts或深入源码 packages/effect/src/BigDecimal.ts 查看完整实现。【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价