C++ Int128 —— 128位有符号整数类实现剖析

🧠 C++ Int128 —— 128位有符号整数类实现剖析

引用:openppp2/ppp/Int128.h

🏗️ 1. 存储结构设计

Int128
+unsigned long long lo: 低64位数据存储
+signed long long hi: 高64位数据存储(带符号)
+...: 其他运算符
+Int128()
+Int128(signed char)
+Int128(unsigned long long)
+Int128(signed long long, unsigned long long)
+operator=()
+operator+()
+operator-()

内存布局解析

0        64       128 (位)
┌────────┬────────┐
│   lo   │   hi   │
└────────┴────────┘

设计特点

  • #pragma pack(push, 1) 确保16字节紧凑存储
  • 低64位(lo)使用无符号类型处理纯数据
  • 高64位(hi)使用有符号类型处理数值符号
  • 构造函数覆盖所有基础整数类型

➕ 2. 加法运算实现

算法流程图

溢出
未溢出
开始
计算低64位和
检测低64位溢出
高64位加1
直接计算高64位和
组合结果
返回结果

代码实现详解

inline Int128 operator+(const Int128& left, const Int128& right)
{Int128 value; // 创建临时结果对象value.lo = left.lo + right.lo; // 计算低64位和// 检测低64位是否溢出// 无符号数溢出时:a + b < aif (value.lo < left.lo) {value.hi = left.hi + right.hi + 1; // 进位处理} else {value.hi = left.hi + right.hi; // 无进位}return value;
}

关键技术点

  • 无符号整数溢出检测:a + b < a
  • 进位处理:高64位额外加1
  • 时间复杂度:O(1),性能接近原生加法

➖ 3. 减法运算实现

算法流程图

开始
对右操作数取负
执行加法操作
返回结果

取负操作实现

inline Int128 Int128::operator-() const
{Int128 x = *this; // 创建副本x.Negate(); // 执行取反操作return x;
}void Int128::Negate()
{hi = ~hi; // 高64位按位取反lo = ~lo; // 低64位按位取反(*this) += 1; // 加1完成补码转换
}

减法运算符实现

inline Int128 operator-(const Int128& left, const Int128& right)
{return left + (-right); // 利用补码原理:a - b = a + (-b)
}

关键技术点

  • 基于补码原理实现减法
  • 取负操作:按位取反后加1
  • 边界情况处理:最小负数取负后仍为自身

✖️ 4. 乘法运算实现

算法流程图

开始
处理操作数符号
分解为32位数组
双层循环乘法
处理进位
组合结果
应用符号
返回结果

代码实现详解

inline Int128 Int128::Multiply(Int128 left, Int128 right)
{// 处理符号int leftSign = left.Sign(); // 获取左操作数符号left = leftSign < 0 ? -left : left; // 转换为正数int rightSign = right.Sign(); // 获取右操作数符号right = rightSign < 0 ? -right : right; // 转换为正数// 分解为32位数组unsigned int xInts[4]; // 左操作数32位数组unsigned int yInts[4]; // 右操作数32位数组memcpy(xInts, &left.lo, 16); // 复制内存memcpy(yInts, &right.lo, 16); // 复制内存unsigned int mulInts[8] = {0}; // 乘积结果数组// 双层循环乘法for (int i = 0; i < 4; i++) {unsigned long long carry = 0; // 进位值for (int j = 0; j < 4; j++) {// 计算32位块乘积unsigned long long product = (unsigned long long)xInts[i] * yInts[j];// 累加乘积和进位unsigned long long sum = product + mulInts[i+j] + carry;// 存储低32位mulInts[i+j] = (unsigned int)sum;// 计算新进位carry = sum >> 32;}// 处理剩余进位int k = i + 4;while (carry) {unsigned long long sum = (unsigned long long)mulInts[k] + carry;mulInts[k] = (unsigned int)sum;carry = sum >> 32;k++;}}// 应用符号并返回结果return Int128(leftSign * rightSign, mulInts, 8);
}

关键技术点

  • 32位块分解避免64位乘法溢出
  • 小学乘法算法实现
  • 双重循环处理所有位组合
  • 进位链式处理

➗ 5. 除法与取模运算实现

算法流程图

开始
处理操作数符号
分解为32位数组
除数为单字?
简单除法
归一化操作数
Knuth算法D
试商计算
乘减操作
结果负?
加回调整
存储商
反归一化
组合结果
应用符号
返回结果

核心代码实现

inline Int128 Int128::DivRem(Int128 dividend, Int128 divisor, Int128& remainder)
{// 处理除数为零if (divisor == 0) return 0;// 处理符号int dividendSign = dividend.Sign();dividend = dividendSign < 0 ? -dividend : dividend;int divisorSign = divisor.Sign();divisor = divisorSign < 0 ? -divisor : divisor;// 分解为32位数组unsigned int u[4]; // 被除数数组unsigned int v[4]; // 除数数组memcpy(u, &dividend.lo, 16);memcpy(v, &divisor.lo, 16);unsigned int quotient[4] = {0}; // 商数组unsigned int rem[4] = {0}; // 余数数组// 执行无符号除法DivModUnsigned(u, v, quotient, rem);// 构造结果remainder = Int128(1, rem, 4); // 余数return Int128(dividendSign * divisorSign, quotient, 4); // 商
}

Knuth算法D关键步骤

void Int128::DivModUnsigned(unsigned int* u, unsigned int* v, unsigned int*& q, unsigned int*& r)
{int m = GetLength(u, 4); // 被除数有效长度int n = GetLength(v, 4); // 除数有效长度if (n <= 1) {// 单字除数特殊处理unsigned long long rem = 0;unsigned int v0 = v[0];for (int j = m - 1; j >= 0; j--) {rem = (rem << 32) + u[j]; // 组合当前值q[j] = (unsigned int)(rem / v0); // 计算商rem %= v0; // 更新余数}r[0] = (unsigned int)rem;}else if (m >= n) {// 归一化int shift = GetNormalizeShift(v[n-1]);unsigned int un[4] = {0};unsigned int vn[4] = {0};Normalize(u, m, un, shift);Normalize(v, n, vn, shift);// Knuth算法D主循环for (int j = m - n; j >= 0; j--) {// 试商计算unsigned long long rr = (unsigned long long)un[j+n] * Base32 + un[j+n-1];unsigned int qhat = (unsigned int)(rr / vn[n-1]);unsigned long long rhat = rr % vn[n-1];// 试商调整while (qhat >= Base32 || (qhat * vn[n-2] > (rhat * Base32 + un[j+n-2]))) {qhat--;rhat += vn[n-1];if (rhat >= Base32) break;}// 乘减操作signed long long borrow = 0;for (int i = 0; i < n; i++) {unsigned long long p = (unsigned long long)qhat * vn[i];signed long long t = un[i+j] - borrow - (p & 0xFFFFFFFF);un[i+j] = (unsigned int)t;borrow = (p >> 32) - (t >> 32);}borrow = un[j+n] - borrow;un[j+n] = (unsigned int)borrow;// 结果调整if (borrow < 0) {qhat--;unsigned long long carry = 0;for (int i = 0; i < n; i++) {carry = (unsigned long long)vn[i] + un[i+j] + carry;un[i+j] = (unsigned int)carry;carry >>= 32;}un[j+n] += (unsigned int)carry;}// 存储商q[j] = qhat;}// 反归一化Unnormalize(un, r, shift);}else {// 被除数小于除数memset(q, 0, 16); // 商为零memcpy(r, u, 16); // 余数为被除数}
}

关键技术点

  • 归一化提高数值稳定性
  • 试商算法减少迭代次数
  • 乘减操作实现精确计算
  • 反归一化恢复正确余数

🧮 6. 位运算实现

6.1 按位取反 (~)

输入值
对lo取反
对hi取反
组合结果
inline Int128 operator~(const Int128& value)
{return Int128(~value.hi, ~value.lo); // 高低位分别取反
}

6.2 左移 (<<)

开始
处理零位移
计算块移位
计算位内移位
处理低64位
处理高64位
处理跨块位移
组合结果
返回结果
inline Int128 operator<<(const Int128& value, int shift)
{// 处理零位移if (shift == 0) return value;// 计算实际位移shift %= 128; // 规范化位移// 分解为64位块unsigned long long* values = (unsigned long long*)&value.lo;// 计算块移位和位内移位int shiftOffset = shift / 64; // 整块移位int bshift = shift % 64; // 块内移位unsigned long long shifted[2] = {0}; // 结果数组// 处理低64位if (shiftOffset == 0) {shifted[0] = values[0] << bshift;if (bshift > 0) {shifted[1] = values[0] >> (64 - bshift);}}// 处理高64位if (shiftOffset <= 1) {int idx = shiftOffset;shifted[idx] |= values[1] << bshift;if (bshift > 0 && idx < 1) {shifted[idx+1] = values[1] >> (64 - bshift);}}return Int128((signed long long)shifted[1], shifted[0]);
}

6.3 右移 (>>)

开始
处理零位移
处理符号扩展
计算块移位
计算位内移位
处理高64位
处理低64位
处理跨块位移
组合结果
返回结果
inline Int128 operator>>(const Int128& value, int shift)
{// 处理零位移if (shift == 0) return value;// 计算实际位移shift %= 128; // 规范化位移// 处理整块移位unsigned long long* values = (unsigned long long*)&value.lo;if (shift >= 64) {int blockShift = shift / 64;shift %= 64;// 移动整块values[0] = values[1];// 符号扩展values[1] = (unsigned long long)((signed long long)values[1] >> 63);}// 块内移位处理int bshift = 64 - shift; // 补位位数unsigned long long shifted[2] = {0};// 高64位处理(带符号扩展)shifted[1] = (unsigned long long)((signed long long)values[1] >> shift);// 低64位处理shifted[0] = values[0] >> shift;if (shift > 0) {shifted[0] |= values[1] << bshift;}return Int128((signed long long)shifted[1], shifted[0]);
}

6.4 按位与 (&)、或 (|)、异或 (^)

操作数A
lo部分运算
hi部分运算
组合结果
// 按位与
inline Int128 operator&(const Int128& left, const Int128& right)
{return Int128(left.hi & right.hi, left.lo & right.lo);
}// 按位或
inline Int128 operator|(const Int128& left, const Int128& right)
{return Int128(left.hi | right.hi, left.lo | right.lo);
}// 按位异或
inline Int128 operator^(const Int128& left, const Int128& right)
{return Int128(left.hi ^ right.hi, left.lo ^ right.lo);
}

🛠️ 7. 关键辅助算法实现

7.1 归一化技术

开始
计算移位值
左移操作数
处理进位
返回归一化结果
inline void Int128::Normalize(unsigned int* u, int l, unsigned int* un, int shift)
{unsigned int carry = 0; // 进位值if (shift > 0) {int rshift = 32 - shift; // 右移位数for (int i = 0; i < l; i++) {unsigned int ui = u[i]; // 当前值un[i] = (ui << shift) | carry; // 左移并合并进位carry = ui >> rshift; // 计算新进位}// 处理剩余进位if (carry != 0) {un[l] = carry;}} else {// 无移位直接复制for (int i = 0; i < l; i++) {un[i] = u[i];}}
}

7.2 长度计算

开始
从高位扫描
找到第一个非零值
返回有效长度
inline int Int128::GetLength(unsigned int* uints, int uintslen)
{int index = uintslen - 1; // 从最高位开始// 跳过前导零while (index >= 0 && uints[index] == 0) {index--;}return index + 1; // 返回有效长度
}

🔄 8. 类型转换实现

在这里插入图片描述

// 布尔转换
inline Int128::operator bool() const
{return lo != 0 || hi != 0; // 任意位非零即为真
}// 64位整数转换
inline Int128::operator unsigned long long() const
{return lo; // 仅使用低64位
}// 32位整数转换
inline Int128::operator unsigned int() const
{return (unsigned int)lo; // 截断到32位
}

📝 9. 字符串转换实现

9.1 数值转字符串

开始
处理符号
转换为无符号数
进制转换
字符映射
构建字符串
返回结果

9.2 字符串转数值

开始
跳过空白
处理符号
按位解析
进制转换
累加结果
应用符号
返回结果

🏁 总结

Int128类的实现展示了以下核心技术:

  1. 紧凑存储结构:16字节内存布局
  2. 算术运算实现
    • 加法:溢出检测与进位处理
    • 减法:补码转换技巧
    • 乘法:32位块分解与进位链
    • 除法:Knuth算法D与归一化技术
  3. 位运算实现
    • 移位:块处理与跨块位移
    • 逻辑运算:分高低位处理
  4. 辅助算法
    • 归一化提高数值稳定性
    • 有效长度计算优化性能
  5. 类型转换
    • 显式转换保证安全性
    • 低64位截断处理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/96146.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/96146.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C 语言生成指定范围随机数(整数 + 小数):原理、实现与避坑指南】

概述 在 C 语言开发中&#xff0c;生成指定范围的随机数是高频需求&#xff08;如游戏随机道具、数据模拟、测试用例生成等&#xff09;。但很多新手会卡在 “范围控制”“随机数重复”“小数生成” 等问题上。本文结合实战场景&#xff0c;从原理到代码详细讲解如何生成 1100、…

一个简单的langgraph agent系统

本文基于langgraph的预制件 Agent Chat UI和《搭建一个本地langgraph服务》中的本地服务构建一个简单的agent系统。 说明&#xff1a;Agent Chat UI需要nodejs版本18及以上&#xff0c;而nodejs18需要的glibc版本为2.28&#xff0c;本人使用操作系统为ubuntu18.04&#xff0c;g…

通过SSH来推送本地文件夹到Github

配置SSH git使用SSH配置&#xff0c; 初始需要以下三个步骤 使用秘钥生成工具生成rsa秘钥和公钥 将rsa公钥添加到代码托管平台 将rsa秘钥添加到ssh-agent中&#xff0c;为ssh client指定使用的秘钥文件 具体操作如下&#xff1a; 第一步&#xff1a;检查本地主机是否已经存在…

视频转webp批量处理工具哪个好?这里有答案

你是不是也遇到过这样的困扰&#xff1a;手机里存满了精彩的短视频&#xff0c;想做成动图分享到社交媒体&#xff0c;却发现转换后的GIF文件巨大无比&#xff0c;画质还惨不忍睹&#xff1f;要怎么把手机视频转webp&#xff0c;才能既保持高清画质&#xff0c;又能大幅减小文件…

【Fastjson】Fastjson2 在不同 Modules 模块包下,@JSONField name映射无法反序列化的 BUG 及解决

问题&#xff1a;在使用 alibaba fastjson2 做 JSONField 字段名映射时&#xff0c;在同模块包下 Flink Jar 任务正常映射&#xff0c;本地测试正常映射&#xff0c;但是将两个模块包上传至 Flink Cluster 之后&#xff0c;出现反序列化异常&#xff0c;子模块无法反序列化父模…

Go语言基础---数据类型间的故事

Go语言基础—数据类型间的故事 目录 前言基本数据类型 整形字节特殊整形unsafe.Sizeof数字字面量语法浮点型布尔值字符串byte和rune类型 运算符 算术运算符关系运算符逻辑运算符位运算符赋值运算符 前言 Go语言是Google开发的一种静态强类型、编译型语言。Go语言语法与C相近…

dedecms软件等级★号改成图片图标显示的办法

我们在用到dedecms织梦的软件模型&#xff0c;在调用软件星级的时候&#xff0c;要把默认的星号改为图片&#xff0c;这个要怎么操作呢&#xff1f;1、软件模型管理里面-字段管理-字段配置softrankislink一行改为&#xff1a;<field:softrank itemname软件等级 typeint isnu…

windows下安装claude code+国产大模型glm4.5接入(无需科学上网)

下载安装node.js https://nodejs.org/en/download 安装版.msi 直接下载安装即可 免安装版.zip 1.解压下载的压缩包 2.创建数据缓存存储目录cache和全局安装工具目录global 3.配置环境变量 【我的电脑】右键选中【属性】-> 找到【高级系统设置】-> 右下角【环境变量…

嵌入式 - ARM4

裸机实现LED闪烁一、启动代码1. 异常向量表配置1. .global汇编器指令&#xff0c;全局定义标签_start&#xff0c;作为汇编程序的默认起点2. 配置标签配置标签时可以前置加_ &#xff0c;以便和普通标签或系统标签做区分3. 异常向量表ARM架构规定异常向量表位置固定&#xff0c…

《C++ 108好库》之2 多线程库thread,mutex,condition_variable,this_thread

《C 108好库》之之2 多线程库thread&#xff0c;mutex&#xff0c;condition_variable&#xff0c;this_thread《C 108好库》之2 多线程库thread&#xff0c;mutex&#xff0c;condition_variable&#xff0c;this_threadstd::thread类​​互斥量&#xff08;Mutex&#xff09;…

Android系统框架知识系列(二十):专题延伸:JVM vs ART/Dalvik - Android运行时演进深度解析

​关键词​&#xff1a;运行时优化、AOT编译、JIT编译、内存管理、电池效率、性能分析一、Android运行时演进背景1. 移动环境的特殊挑战Android运行时环境的演进源于移动设备的独特限制&#xff1a;​移动设备约束条件​&#xff1a;​有限的内存资源​&#xff1a;早期设备仅1…

ubuntu 22 安装轻量级桌面Xfce并使用xrdp远程桌面连接

1.安装Xfce:sudo apt install xubuntu-desktop -y2.安装xrdp:sudo apt install xrdp -y3.配置xrdp&#xff0c;nano /etc/xrdp/xrdp.ini:[Globals] ... port3389 ; 远程连接端口&#xff0c;默认是3389&#xff0c;可以改成自己喜欢的端口... ; ; Session types ;; Some sess…

【Flask】测试平台开发,数据看板开发-第二十一篇

概述&#xff1a;在前面我们已经实现了我们的产品创建管理&#xff0c;应用管理管理&#xff0c;需求提测管理但是每周提测了多少需求&#xff0c;创建了哪些产品&#xff0c;我们是不是看着不是很直观&#xff0c;接下来我们就需要开发一个数据看板功能&#xff0c;实现能够看…

我是程序员,不是程序猿:请别把我当猴耍——拒绝被低估,用专业赢得尊重

摘要 本文旨在深度剖析“程序员”与“程序猿”一字之差背后所反映的职业尊严与身份认同问题。我们生活在一个技术驱动的时代&#xff0c;但对技术创造者的认知却常常被“程序猿”、“码农”等标签简单化、甚至矮化。本文将从正名开始&#xff0c;辨析“程序员”的专业内涵&…

C++中vector删除操作的安全隐患与最佳实践

std::vector 是C标准模板库&#xff08;STL&#xff09;中最常用的动态数组容器&#xff0c;提供了高效的随机访问和动态扩容能力。然而&#xff0c;其删除操作如果使用不当&#xff0c;会引入严重的安全隐患&#xff0c;包括未定义行为、内存泄漏和数据竞争等问题。本文将深入…

Unix/Linux 系统中的 `writev` 系统调用

<摘要> 本文对 Unix/Linux 系统中的 writev 系统调用进行了全面深入的解析。内容涵盖了其产生的背景&#xff08;从传统 write 的局限性到分散/聚集 I/O 概念的引入&#xff09;、核心概念&#xff08;如 struct iovec、系统调用流程&#xff09;。重点剖析了其设计意图&…

深入理解 Android targetSdkVersion:从 Google Play 政策到依赖冲突

深入理解 Android targetSdkVersion&#xff1a;从 Google Play 政策到依赖冲突 作为 Android 开发者&#xff0c;你很可能在 Android Studio 中见过这条提示&#xff1a;Google Play requires that apps target API level 33 or higher。它像一个尽职的提醒者&#xff0c;时常…

灰匣(GrayBox)1.0.0 发布【提升系统权限APP】

灰匣是一个提升系统权限的工具&#xff0c;可以配合Root、三方软件&#xff08;Shizuku&#xff09;以及【设备管理员】&#xff08;设备所有者&#xff09;实现一些高级功能及底层接口&#xff0c;可以自动隔离&#xff08;冻结/禁用&#xff09;不必要的应用&#xff0c;如某…

PAT 1104 Sum of Number Segments

这一题的大意就是找一个数组中的所有子数组&#xff0c;它们的累加和为多少&#xff0c; 题目上给出的数据范围是O(n^5)那么只能遍历一次&#xff0c;不能用暴力的方法求出。 看到这一题我有两个思路&#xff1a; 1.试图用双指针和滑动窗口来把O&#xff08;n^2)的时间复杂度降…

[万字长文]AJAX入门-常用请求方法和数据提交、HTTP协议-报文、接口文档、案例实战

本系列可作为前端学习系列的笔记&#xff0c;代码的运行环境是在VS code中&#xff0c;小编会将代码复制下来&#xff0c;大家复制下来就可以练习了&#xff0c;方便大家学习。 HTML、CSS、JavaScript系列文章 已经收录在前端专栏&#xff0c;有需要的宝宝们可以点击前端专栏查…