大数(BigNumber)

以太坊中的许多操作都是对超出JavaScript安全值范围的数字进行操作。

BigNumber是一个可以安全地对任意大小的数字进行数学运算的对象。

大多数需要返回值的操作将返回一个BigNumber,接受值的参数通常会接收它们。

类型

BigNumberish

这个库中的许多函数和方法都接受可以无歧义(non-ambiguously)且安全地转换为BigNumber的值。这些值可以指定为:

string

HexString或decimal string,两者都可以为负数。

BytesLike

一个BytesLike对象,例如数组或Uint8Array。

BigNumber

一个大数(BigNumber)实例。

number

一个在JavaScript numbers的安全范围内的数字。

BigInt

在支持BigInt的环境中的一个JavaScript BigInt对象。

创建实例

BigNumber的构造函数不能被直接调用。相反,使用静态BigNumber.from

ethers.BigNumber.from( aBigNumberish ) 大数(BigNumber)

aBigNumberish返回一个BigNumber的实例。

例子:

// 来自于十进制字符串 BigNumber.from("42") // { BigNumber: "42" } // 来自于十六进制 BigNumber.from("0x2a") // { BigNumber: "42" } // 来自于负数的十六进制 BigNumber.from("-0x2a") // { BigNumber: "-42" } // 来自于 数组 或 Uint8Array BigNumber.from([ 42 ]) // { BigNumber: "42" } // 来自于BigNumber let one1 = constants.One; let one2 = BigNumber.from(one1) one2 // { BigNumber: "1" } // 返回相同的实例 one1 === one2 // true // 来自于(安全) 的数字... BigNumber.from(42) // { BigNumber: "42" } // 来自于 ES2015 BigInt (只能在支持BigInt的平台上使用) BigNumber.from(42n) // { BigNumber: "42" } // 安全范围外的数字将会失效: BigNumber.from(Number.MAX_SAFE_INTEGER); // [Error: overflow [ See: https://links.ethers.org/v5-errors-NUMERIC_FAULT-overflow ]] { // code: 'NUMERIC_FAULT', // fault: 'overflow', // operation: 'BigNumber.from', // reason: 'overflow', // value: 9007199254740991 // }

方法

BigNumber类是不可变的,所以没有任何操作可以改变它所表示的值。

数学运算

BigNumber.add( otherValue ) 大数(BigNumber)

返回值为BigNumber + otherValue的BigNumber。

BigNumber.sub( otherValue ) 大数(BigNumber)

返回值为 BigNumber - otherValue的BigNumber。

BigNumber.mul( otherValue ) 大数(BigNumber)

返回值为 BigNumber × otherValue的BigNumber。

BigNumber.div( divisor ) 大数(BigNumber)

返回值为 BigNumber ÷ divisor的BigNumber。

BigNumber.mod( divisor ) 大数(BigNumber)

返回值为 BigNumber ÷ divisor余数的BigNumber。

BigNumber.pow( exponent ) 大数(BigNumber)

返回值为 BigNumber 指数的幂为exponent的BigNumber。

BigNumber.abs( ) 大数(BigNumber)

返回值为绝对值的BigNumber。

BigNumber.mask( bitcount ) 大数(BigNumber)

返回一个BigNumber,其BigNumber的值超出bitcount最低有效位的位则设为0。

Two's Complement

Two's Complement是一种优雅的方法, 用于编码和解码固定宽度的有符号值,同时有效地保留数学运算。大多数用户不需要与它们交互。

BigNumber.fromTwos( bitwidth ) 大数(BigNumber)

返回一个BigNumber, BigNumber的值由带位宽(bitwidth)的二进制补码转换而来。

BigNumber.toTwos( bitwidth ) 大数(BigNumber)

返回一个BigNumber, BigNumber的值转换为带位宽的二进制补码。

比较和相等

BigNumber.eq( otherValue ) boolean

当且仅当BigNumber的值等于otherValue时返回true。

BigNumber.lt( otherValue ) boolean

当且仅当BigNumber的值<otherValue时返回true。

BigNumber.lte( otherValue ) boolean

当且仅当BigNumber的值otherValue时返回true。

BigNumber.gt( otherValue ) boolean

当且仅当BigNumber的值>otherValue时返回true。

BigNumber.gte( otherValue ) boolean

当且仅当BigNumber的值otherValue时返回true。

BigNumber.isZero( ) boolean

当且仅当BigNumber的值为0时返回true。

转换

BigNumber.toBigInt( ) bigint

在支持BigInt的平台上以JavScript BigInt值返回BigNumber的值。

BigNumber.toNumber( ) number

将BigNumber的值转换JavaScript值。

如果该值大于Number.MAX_SAFE_INTEGER或小于等于Number.MIN_SAFE_INTEGER, 则会抛出一个错误

BigNumber.toString( ) string

以十进制字符串的形式返回BigNumber的值。

BigNumber.toHexString( ) string< DataHexString >

返回BigNumber的值为十六进制的值,0x是前缀DataHexString.。

检查

ethers.BigNumber.isBigNumber( object ) boolean

当且仅当对象是BigNumber是对象时返回true。

例子

let a = BigNumber.from(42); let b = BigNumber.from("91"); a.mul(b); // { BigNumber: "3822" }

注意

这部分是针对一些经常出现的问题。

为什么我不能只使用 numbers?

许多人在处理以太坊时遇到的第一个问题是数字的概念。 大多数常见的货币被划分成非常小的粒度。例如,1美元只有100美分。然而,一个ether等于1018 wei

JavaScript使用IEEE 754 double-precision binary floating point](link-wiki-ieee754)来表示数值。 因此,在9,007,199,254,740,991之后的整数集就存在漏洞了; 这对以太坊来说是个问题,即使是9,007,199,254,740,991数值也才只有0.009以太(单位:wei), 这意味着超过这个值将开始产生舍入误差。

用代码来说明这个问题,如下:

(Number.MAX_SAFE_INTEGER + 2 - 2) == (Number.MAX_SAFE_INTEGER) // false

为了修复这一点,所有的数字(可以很大)都被存储和操作为Big Numbers](BigNumber)。

函数parseEther( etherString )formatEther( wei ) 可以用字符串(用户可以查看或输入)和Big Number(可以安全地进行数学操作)之间进行转换。

为什么不用 BigNumber.js, BN.js, BigDecimal, 这一类?

每个人都有自己最喜欢的Big Number库,一千个读者就有一千个哈姆雷特, 就像他们的编辑器都有vi vs emacs。在npm上有超过100个Big Number库。

Ethers 大数(BigNumber)对象和其他库之间最大的区别之一是它是不可变的,这在处理区块链的异步特性时非常重要。

在异步函数中捕获值是不安全的,所以不可变性可以让我们不容易犯错误,这在支持大量就地操作的低级库上是不可能得到保证的。

其次,Ethers 大数(BigNumber)提供了内部所需的所有功能,通常对大多数开发人员来说应该足够了, 而不暴露一些更高级和罕见的功能。因此,在不影响用户的情况下,交换底层库将更加容易。

例如,如果BN.js被公开,有人可能会使用最大的公分母函数, 这将是替换库也应该提供的功能,以确保不会影响依赖于该功能的开发人员。

为什么是BN.js??

BN.js之所以在内部被用作big number,是因为它是elliptic使用的库。

因此,无论如何都必须包含它,所以我们利用这个库,而不是添加另一个Big Number库,添加了就意味着两个不同的库提供相同的功能。

与其他库(包括用于各种目的的单独的Big Number库)相比,这节省了大约85kb(该库大小的80%)的库大小。

允许我们设置一个全局的 Big Number 库吗?

有些人经常提到希望指定一个用户定义的全局 Big Number库,所有函数都将返回这个库。

这是有问题的,因为您的代码可能与其他使用Ethers的库或代码共存。事实上,甚至Ethers也在内部使用了许多公共函数。

例如,如果你使用的库使用的是a.plus(b)而不是a.add(b),这将在尝试内部计算fees时Ethers会被破坏,而其他库可能有类似的逻辑。

但是,大数(BigNumber)原型是公开的,所以你总是可以添加一个toMyCustomBigNumber()方法到所有全局的大数(BigNumber)会更安全。