起因:今天在调用合约的时候发现使用 BIgInt 丢了精度。看了下发现是自己的姿势不对,记录一下问题。
一、错误操作
const amountIn = '2e+24'
const contract = contract.function(BigInt(Number('2e+24'))
为什么会这么写呢, 因为我们前端库升级到了 ethersJS V6 版本,v6 里把 BigNumber 迁移到了 bigInt,所以我潜意识里将传的参数都转换成 BigInt 格式。而且潜意识里BigInt能处理任意精度。但是这一行代码里就踩了几个坑。
- 我没有 etherJS 库里的 getBigInt 方法,但凡用了这个方法我就会发现报 overflow 异常。 JS 里 BigInt 的最大值:
// IEEE 754 support 53-bits of mantissa
// 等于 JS 里 Number.MAX_SAFE_INTEGER: 9_007_199_254_740_991
const maxValue = 0x1fffffffffffff;
错误: Number(‘2e+24’);超过的 上述的安全值后,即会产生数据失真。
2. 概念混淆, BigInt确实能处理任意精度,但是 Number 的最大值是有限的, 不能拿错误的 Number 去给 BigInt 处理,这必❌
问:超过安全值再计算会发生什么? 失真。 这里贴了网上的一个栗子。
const max = Number.MAX_SAFE_INTEGER;
// → 9_007_199_254_740_991
max + 1;
// → 9_007_199_254_740_992 ✅
max + 2;
// → 9_007_199_254_740_992 ❌
修改
- 方法一: BigNumber(‘2e24’).toString(10) 将科学技术法用字符串展开。其实到这里就可以直接传给合约了
1.1 如果要转 bigInt格式则:BigInt(‘2000000000000000000000000’); 直接用字符串转 - 方法二: ethers. parseEther(‘2000000’) 直接用标准方法,省的换算麻烦
参考链接🔗:
etherJS仓库
BigInt:JavaScript 中的任意精度整数