UIntX

Swift 5.2 Swift Package Manager License: MIT GitHub tag Run Tests

UIntX 是一种能够统治所有无符号整数的类型。它几乎没有任何大小限制,可以用来表示任何数字。 因此,您可以存储 64 位(大多数现代计算机上的默认值)、128 位、256 位、512 位、1024 位、2048 位,实际上任何您想要的大小的无符号整数都可以存储在这一个容器中。

用法

UIntX 基本上是一个用于存储预定义基本值的多个“字”(word)的容器。因此,它是一个泛型结构体,期望一个 FixedWidthInteger & UnsignedInteger 作为其特定的基本值。 目前,Swift 的标准库中存在符合这些协议的 UInt8UInt16UInt32UInt64UInt,当然也可以使用其他结构体,只要它们符合指定的协议。 库中提供了四个类型别名,如下所示:

typealias UIntX8 = UIntX<UInt8>
typealias UIntX16 = UIntX<UInt16>
typealias UIntX32 = UIntX<UInt32>
typealias UIntX64 = UIntX<UInt64>

它们仅为了方便起见,因此 UIntX 已经被专门化。 您可以使用它们,也可以使用 UIntX 的完整声明来专门化它的基本值,例如 UIntX<UInt>

有两种推荐的初始化 UIntX 的方式。 第一种是最直接的,您只需提供一个 UnsignedInteger 作为初始化程序的唯一参数,例如:

let unsignedInteger: UInt = 123
let uintx = UIntX8(unsignedInteger)

UIntX8 也符合 ExpressibleByIntegerLiteral 协议,因此您可以像这样初始化它:

let uintx: UIntX8 = 123

第二种推荐的初始化方式是通过一个值数组,其中数组的第一个元素是最低有效位,数组的最后一个元素是最高有效位

let array: [UInt8] = [0x89, 0x67, 0x45, 0x23, 0x01]
let uintx = UIntX8(littleEndianArray: array)
uintx == 0x0123456789 // true

请注意,在这两种初始化方法中,提供的数值如果高于基本值可以处理的范围,会被分解为基本值可以处理的较小值,这是自动完成的,因此您可以将任何值放入 UIntX 中。 使用 init(littleEndianArray:) 初始化器时,有一个需要注意的陷阱,如果提供的数组使用具有更大数量级的元素(其中 N == ArrayElement.bitWidth / BaseElement.bitWidth),则数组的每个元素将被分解为 N 个基本元素的“字”,或者提供的数组中的元素将被打包为等效的 bitWidth 基本元素的“字”,这是一个使用不同基本值的示例:

let array: [UInt64] = [1, 2]
let uintx8 = UIntX8(littleEndianArray: array)
let uintx64 = UIntX64(littleEndianArray: array)

uintx8 == 0x020000000000000001 // since each element has 64 bits they will each be transformed into 8 words of 8 bits. The leading zeros are truncated
uintx64 == 0x00000000000000020000000000000001

// OR

let array: [UInt8] = [1, 2]
let uintx8 = UIntX8(littleEndianArray: array)
let uintx64 = UIntX64(littleEndianArray: array)

uintx8 == 0x0201
uintx64 == 0x0000000000000201 // since each element has 8 bits they can be packed into the same word of the base element.

一旦您有了 UIntX,您可以像使用任何其他无符号二进制整数值一样使用它,这意味着大多数常见操作都是可用的。

let binary: UIntX8 = 0b0001_0010
binary << 1     // 0b0010_0100
binary >> 1     // 0b0000_1001
binary & 0b1111 // 0b0000_0010
binary | 0b1111 // 0b0001_1111
binary ^ 0b1111 // 0b0001_1101
~binary         // 0b1110_1101

let value: UIntX8 = 123
value / 4       // 30
value % 4       // 3
value * 4       // 492
value + 4       // 127
value - 4       // 119
value > 4       // true
value < 4       // false
value == 4      // false
value != 4      // true

UIntX 的技术限制

UIntX 基本上是一个用于存储基本值“字”的容器。 为此,我们将这些“字”存储在一个数组中(内部称为 parts)。 Swift 中的数组默认使用 Int 索引,这意味着您只能在数组中存储 Int 本身可以处理的元素数量(即 Int.max)。 由于 Int 是一个 SignedNumber,因此它的一个位(最高有效位)用于存储符号信息,这使得我们拥有 Int.max,对于 64 位操作系统来说是 263。 如果我们使用 UInt64 (64 位) 的基本值,则意味着每个“字”将能够存储 64 位,因此如果我们有 1 个“字”,我们将有一个 64 位的数字,如果我们有 2 个“字”,我们将有一个 128 位的数字,依此类推。

let baseValueBitWidth = 64 // base value number of bits
let indexes = 2^63 // maximum number of words that can be stored
let totalNumberOfBits = baseValueBitWidth * indexes // 64 * 2^63 = 2^6 * 2^63 = 2^69

因此,最终我们将得到一个具有269的数字,这意味着我们可以达到的最大数字是2(269)。 这是一个巨大的数字,让我们尝试将其转换为以 10 为底的幂数,以便更容易理解它的值。

每次将 2 乘以 10 的倍数时,结果数可以大致转换为 10 的 3 次幂。

210 = 1,024 ~ 103 = 1,000

220 = 1,048,576 ~ 106 = 1,000,000

230 = 1,073,741,824 ~ 109 = 1,000,000,000

因此,269 可以分解为 270 * 2-1 (= 0.5),这反过来可以近似为:0.5 * 1021

这就是该数字中的位数:大约5000 亿亿位

好的,这是一个巨大的位数,现在让我们将其转换为实际数字

2(5 * 1020) ~ 10(5 * 1019 * 3) = 10(1.5 * 1020) = 10(1020) * 10(5 * 1019) = 10(1020) * 10(1019) * 10(1019) * 10(1019) * 10(1019) * 10(1019)

1020 = 1000 亿亿

1019 = 100 亿亿

1000 亿亿 + 100 亿亿 + 100 亿亿 + 100 亿亿 + 100 亿亿 + 100 亿亿

那是1500 亿亿

顺便说一句,这只是数字的幂,所以实际数字将是

101500 亿亿

希望我的数学运算是正确的,但无论如何,很明显我们可以用这个结构体表示一个非常大的数字。

话虽如此,我正在强制限制可以存储的“字”的数量,这是通过以下方式完成的:

UIntXConfig.maximumNumberOfWords = 128

我这样做是为了安全起见,因为我不知道一个程序在必须保存如此大的数字时会如何表现。

您可以随时更改此值,但风险自负,我还没有测试过使用超过 8,192 位(已经是一个非常大的数字)的数字的 UIntX,因此我建议坚持该限制,除非您知道自己在做什么。

安装

使用 Swift Package Manager

UIntX 作为依赖项添加到您的 Package.swift 文件中。 有关更多信息,请参阅 Swift Package Manager 文档

.package(url: "https://github.com/rkreutz/UIntX", from: "1.0.0")

帮助 & 反馈

路线图