64位系统上i128的秘密:Rust如何处理巨型数字

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:小世界中的大数字



想象一下:您正在编写一个加密算法、物理过程模拟,或者只是计算到最近星系的距离(以纳米为单位)。突然您意识到——标准的i64(从-9百亿亿到9百亿亿)完全不够用。Rust中有一个救生圈——i128类型。但它究竟如何在物理上无法处理128位数字的处理器上工作?这难道是魔法吗?



剧透:没有魔法,只有天才的工程学。在本文中,我们将剖析Rust(更准确地说,是底层的LLVM)如何在64位架构上模拟i128。您将了解到为什么它并不像看起来那么慢,以及如何使用这些巨型数字编写高效代码。这不是枯燥的理论——我们将通过示例,从问题出发,一路走到实际应用。



1. 硬件现实:为什么128位是一个挑战



现代64位处理器(x86-64、ARM64)拥有64位的通用寄存器。这是它们的“原生”字长。要处理128位数字,处理器必须对两个64位半部分(低位和高位)执行多次操作。



在Rust中,i128类型(及其无符号兄弟u128)不是硬件类型,而是一个聚合类型。它在内存中存储为两个相邻的64位字。编译器(通过LLVM)生成将这些字作为一个整体处理的代码。



一个简单的例子:



fn main() {    let big: i128 = 123456789012345678901234567890;    println!("{}", big);}


在汇编层面,这将转化为一系列操作两个64位寄存器(例如rax和rdx)的指令。LLVM非常擅长高效地“拼接”这些操作。



2. 加法剖析:Rust如何相加i128



让我们看看最基本的操作——两个i128相加。编写一个简单的函数,并使用cargo-show-asm或Compiler Explorer一探究竟。



fn add_big(a: i128, b: i128) -> i128 {    a + b}


为x86-64编译(不优化,以便更清晰)。LLVM将生成大致如下的伪代码:



  1. 将a的低64位加载到寄存器rax,高位加载到rdx。
  2. 将b的低64位加载到rbx,高位加载到rcx。
  3. 相加低位部分:add rax, rbx——结果在rax中,如果发生溢出则设置进位标志(CF)。
  4. 考虑进位相加高位部分:adc rdx, rcx——如果CF标志被设置,adc(带进位加法)会加1。
  5. 结果:低64位在rax,高64位在rdx。


这就是模拟。两条指令代替一条。乘法和除法的代码会更复杂,但原理相同——分解为64位操作。



3. 性能如何?迷思与现实



许多人认为i128比i64慢两倍。这不完全正确。是的,每个操作会变成2-3条指令而不是一条。但现代处理器具有:



  • 流水线和超标量架构——每个时钟周期可以执行多条指令。
  • 分支预测——对于比较操作尤其重要。
  • 大缓存——内存操作不受影响,因为i128只占用16字节。


实际测试(Rust,循环中的简单算术):



use std::time::Instant;

fn main() { let n = 100_000_000; // i64 let start = Instant::now(); let m

博客

书籍推荐