☰
仓颉标准库 BigInt 基本属性实战:parse 解析、sign 符号与 bitLen 位长度详解
2026/10/15 17:32:26 网站建设 项目流程
  • 编程语言
  • 语言运行时
  • 标准库
  • 垃圾回收
  • 并发编程

【免费下载链接】cangjie_runtime

仓颉编程语言运行时与标准库。

项目地址:https://gitcode.com/Cangjie/cangjie_runtime
点击查看免费下载

本文以仓颉标准库std.math.numeric包中BigInt的"基本属性示例"为线索,讲解如何通过字符串解析创建BigInt对象,并查询其符号(sign)与二进制位长度(bitLen)两个核心属性。结合开源仓库中BigInt的源码实现,你将掌握BigInt.parse的解析规则、sign与bitLen的底层计算原理,以及负数位长度比正数多 1 位的本质原因,为后续大整数运算、进制转换与密码学位操作打下基础。

示例总览:一行代码创建 BigInt 并查询基本属性

官方文档 bigInt_basic_prop.md 给出了初始化BigInt对象并查询基本属性的完整示例(文档中带有<!-- verify -->标记,说明该示例已经过验证,可直接复制运行):

import std.math.numeric.* main() { let bigInt = BigInt.parse("-123456") println("BigInt 值: ${bigInt}") println("符号: ${bigInt.sign}") println("二进制位长度: ${bigInt.bitLen}") return 0 }

运行结果:

BigInt 值: -123456 符号: -1 二进制位长度: 18

从运行结果可以看到三个关键信息:

  1. BigInt.parse("-123456")成功将十进制字符串"-123456"转换为BigInt对象;
  2. sign返回-1,表明该值为负数(符号函数的返回值语义是:负数-1、零0、正数1);
  3. bitLen返回18,即用二进制表示-123456最少需要 18 个 bit——这一数值乍看"偏大",其背后正是负数的二进制补码表示规则在起作用,下文会结合源码详细解释。

BigInt 是什么:任意精度有符号整数

BigInt是std.math.numeric包提供的核心结构体,官方概述见 math_numeric_package_overview.md。math.numeric包专门为"基础类型可表达范围之外"的场景提供扩展能力,包括大整数(BigInt)、高精度十进制数(Decimal)以及相应的高精度运算规则。

从源码 big_int.cj 看,其声明为:

public struct BigInt <: Comparable<BigInt> & Hashable & ToString {

也就是说,BigInt实现了Comparable(可比较)、Hashable(可哈希)、ToString(可转字符串)三个接口,因此天然支持==、<、hashCode()、toString()等能力,可以被放进集合、字典,也可以直接通过字符串插值"${bigInt}"打印。

从内部表示看(big_int.cj),BigInt由三部分构成:

  • int: UInt64——存放数值的低 64 位;
  • intArr: Array<UInt32>——存放超出 64 位的高位部分(以 32 位为单位扩展);
  • negSign: Bool——符号标志,true表示负数。

这种"64 位主槽 + 32 位数组扩展"的设计,使得BigInt在数值较小时完全落在UInt64内,性能开销极小;数值超大时才启用数组扩容,从而在性能与任意精度之间取得平衡。sign与bitLen这两个基本属性正是基于这套内部表示计算的。

BigInt.parse:字符串解析的两种形态与语法规则

示例中使用的BigInt.parse(value)是字符串解析的便捷形态,定义在 big_int_extension.cj:

public static func parse(value: String): BigInt { // parse SignString var (isNeg, start) = parseSign(value) // parse BaseString let (radix, baseLength) = (parseBase(value, start)) start += baseLength // parse ValueString let (int, arr) = parseDigits(value, start, radix) if (int == 0 && arr.size == 0) { isNeg = false // no -0 for bigint } return BigInt(int, arr, isNeg) }

从源码注释可以看到,parse(value)接受的字符串遵循如下 BNF 文法:

IntegerString : SignString? BaseString? ValueString
  • SignString:可选的+/-符号前缀;
  • BaseString:可选的进制前缀,支持0b/0B(二进制)、0o/0O(八进制)、0x/0X(十六进制),缺省时按十进制解析,数字字符须与进制匹配;
  • ValueString:数字字符序列(0~9、A~F/a~f等)。

源码中还有一个值得注意的细节:解析结果为 0 时,isNeg会被强制重置为false(注释明确写着no -0 for bigint),即BigInt不存在-0这种形式,BigInt.parse("-0").sign返回0而非-1。

如果字符串格式非法(例如包含非数字字符),parse会抛出IllegalArgumentException。对应地,big_int_extension.cj 提供了tryParse(value)变体:解析失败时不抛异常,而是返回None(可选值),适合需要容错的场景:

public static func tryParse(value: String): ?BigInt { try { return parse(value) } catch (e: Exception) { return None } }

指定进制解析:parse(value, radix)

当需要解析非十进制字符串时,可使用带radix参数的形态(big_int_extension.cj):

public static func parse(value: String, radix!: Int64): BigInt { if (radix < 2 || radix > 36) { throw IllegalArgumentException("Invalid argument 'radix': must be in range [2, 36].") } let (int, intArr, negSign) = parseString(value, UInt64(radix)) return BigInt(int, intArr, negSign) }
  • radix的取值范围是[2, 36],超出该范围会抛出IllegalArgumentException;
  • 数字字符可以是0~9、A~Z、a~z,但每一位都必须小于进制值(源码注释给出了逐字符校验规则:数字字符digit - r'0' < radix,字母字符(digit - r'A') + 10 < radix);
  • 对应还有tryParse(value, radix:)的容错版本(big_int_extension.cj)。

例如BigInt.parse("FF", radix: 16)会得到十进制值255。顺带一提,字符串构造函数BigInt(s: String, base!: Int64 = 10)目前已被标记为@Deprecated,官方推荐统一使用parse系列方法(见 big_int.cj 的废弃说明)。

sign 属性:符号函数(signum)

sign属性返回当前BigInt的符号值,定义在 big_int.cj:

public prop sign: Int64 { get() { if (int == 0 && intArr.size == 0) { return 0 } return if (negSign) { -1 } else { 1 } } }

其返回值语义与 Java 的BigInteger.signum()完全一致:

sign 返回值含义
-1该BigInt为负数
0该BigInt等于零
1该BigInt为正数

实现上,sign首先检查内部表示是否全零(int == 0 && intArr.size == 0),若是则返回0;否则直接依据negSign标志返回-1或1。因此示例中BigInt.parse("-123456").sign返回-1。

在std.math.numeric包中,sign也常用于其他 API 的前置判断,例如 big_int.cj 的isProbablePrime会先检查sign <= 0直接返回false(素数与负数、零无关),可见符号属性是许多数学判断的基础。

bitLen 属性:最短二进制位长度

bitLen返回表示该BigInt所需的最少二进制位数,定义在 big_int.cj。官方 API 文档(math_numeric_package_structs.md)给出的示例语义为:-3(二进制101)返回3,-1(二进制11)返回2,0返回1。

源码中对正数与负数的处理是不同的:

正数(含零)分支:绝对值在[2^n, 2^(n+1))区间时返回n+1。例如8(1000)返回4,15(1111)返回4。

负数分支:negSign为真时,由于负数在计算机中以二进制补码存储,需要额外一位符号位。源码注释给出规则:绝对值在(2^n, 2^(n+1)]区间时返回n+2,例如-5补码为1011(4 位),-8补码为1000(4 位)。只有当负数是 2 的幂时(isPowerOfTwo()为真),才不需要额外补 1 位。

以示例中的-123456为例来验证计算过程:

  1. 绝对值123456满足2^16 = 65536 < 123456 ≤ 2^17 = 131072,即n = 16;
  2. 123456不是 2 的幂,且完全落在 64 位int内(intArr.size == 0);
  3. 走负数分支:64 - leadingZeros(int) + 1 = 17 + 1 = 18,与运行结果二进制位长度: 18完全吻合。

由此可见,对于负数,bitLen反映的是补码表示的实际长度(含符号位),因此通常比绝对值的二进制位数多 1。这个属性在实现密码学算法、位运算优化(如估算BigInt参与模幂运算的规模)时非常实用——BigInt.randomProbablePrime(bitLen, certainty)也正是用bitLen来指定随机素数的位数(见 big_int.cj)。

基本属性的常见搭配:toString、toBytes 与多种构造方式

除sign、bitLen外,BigInt的"基本属性"类查询还有几个高频搭档:

字符串输出:默认的toString()以十进制输出(big_int.cj),而toString(radix:)可输出任意[2, 36]进制的字符串(big_int_extension.cj)。示例中"${bigInt}"打印出-123456正是调用了前者。

字节序列输出:toBytes()返回该BigInt补码形式的大端字节数组(big_int.cj),零值返回单字节[0],常用于序列化与网络传输。

多种构造方式:BigInt不仅可以通过parse从字符串构造,还支持:

  • 直接以各整数类型构造:BigInt(-3)、BigInt(123456789)等(Int8~Int64、UInt8~UInt64、Float16~Float64均提供构造函数);
  • 以补码字节数组构造:BigInt(bytes: Array<Byte>)(大端序);
  • 以符号 + 绝对值构造:BigInt(sign: Bool, magnitude: Array<Byte>),空数组视为 0,且不允许sign = false配零值(会抛IllegalArgumentException);
  • 随机生成:BigInt(sign: Bool, bitLen: Int64, rand!: Random = Random())可生成指定位长的随机大整数(bitLen必须大于 0)。

相关构造函数细节可查阅 math_numeric_package_structs.md。

延伸阅读:从基本属性走向完整 BigInt 能力

掌握了parse、sign、bitLen之后,可以继续深入BigInt的完整能力矩阵:

  • 基础数学运算:四则运算与divAndMod同时获取商和余数,示例见 bigInt_basic_arithmetic.md;
  • 比较操作:BigInt通过Comparable<BigInt>支持==、!=、<、<=、>、>=及compare(other)返回Ordering的语义,示例见 bigInt_compare_opration.md;
  • 位操作与数学函数:testBit/setBit/clearBit/flipBit等位运算(big_int.cj),以及包级函数gcd、lcm、abs、sqrt、countOnes、trailingZeros等(见 math_numeric_package_funcs.md);
  • 溢出策略:BigInt转整数类型时可配合OverflowStrategy枚举(Throwing、Saturating、Wrapping)处理溢出,见 math_numeric_package_enums.md。

小结

BigInt.parse、sign、bitLen构成了使用仓颉BigInt的"第一步":parse解决字符串到任意精度整数的转换(含进制与容错变体),sign提供符号判断,bitLen则给出补码表示的精确位宽。三者背后都有清晰且可验证的源码实现——尤其是负数bitLen比绝对值多 1 位的补码规则,理解它就能准确预估大整数参与位运算和密码学计算的规模,为后续深入BigInt的运算与位操作扫清障碍。

  • 编程语言
  • 语言运行时
  • 标准库
  • 垃圾回收
  • 并发编程

【免费下载链接】cangjie_runtime

仓颉编程语言运行时与标准库。

项目地址:https://gitcode.com/Cangjie/cangjie_runtime
点击查看免费下载

相关推荐

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询