如何高效按小数位拆分uint256为两个无符号整数?
256位无符号整数按指定小数位拆分的轻量级实现方案
需求说明
需要实现轻量级函数splitNumberByDecimals,接收256位无符号整数u和小数位参数dec,将u拆分为整数部分(u除以dec的商)和小数部分(u除以dec的余数)。要求禁止使用字符串转换等高开销操作,优先用位运算或高效算术逻辑实现。
示例:
num = 123456 dec = 100 intFraction = 123 // 123456 // 100 decFraction = 456 // 123456 % 100
对应数学关系:123456 / 100 = 123.456
实现方案
根据dec的类型分两种场景处理,最大化利用位运算提升效率:
1. 当dec是2的幂时(即dec = 2^k)
此时可通过纯位运算实现,完全避免除法开销:
- 整数部分:
intFraction = u >> k(右移k位等价于除以2^k) - 小数部分:
decFraction = u & (dec - 1)(dec-1是低k位全1的掩码,按位与直接取余数)
示例代码:
import "math/bits" func splitNumberByDecimals(u uint256, dec uint) (intFraction, decFraction uint) { k := bits.TrailingZeros(uint(dec)) // 计算2的幂次k intFraction = uint(u >> k) decFraction = uint(u & (uint256(dec) - 1)) return }
2. 当dec是非2的幂时(如10、100等10的幂)
纯位运算无法直接实现十进制除法,但可以用预计算乘法逆元的方式实现轻量级除法,这是区块链等资源受限场景中处理uint256的常用方案:
核心思路
对于256位无符号整数,若dec与2^256互质(即dec为奇数),可预计算其乘法逆元inv,满足dec * inv ≡ 1 mod 2^256。此时:
- 整数部分:
intFraction = (u * inv) >> 256(乘逆元后右移256位等价于除以dec) - 小数部分:
decFraction = u - intFraction * dec
若dec为偶数,先分解为dec = 2^k * m(m为奇数),分两步处理:
- 通过位运算处理
2^k的部分,拆分出临时值和低位余数 - 用预计算的逆元处理奇数
m的部分,最后合并结果
示例实现(以dec=100=2^2*25为例)
import "math/bits" // 预计算25的uint256乘法逆元,通过扩展欧几里得算法得到 const inv25 = uint256(0x1999999999999999999999999999999999999999999999999999999999999999a) func splitNumberByDecimals(u uint256, dec uint) (intFraction, decFraction uint) { if dec == 0 { panic("dec cannot be zero") } // 提取dec中的2的幂因子 k := bits.TrailingZeros(uint(dec)) m := dec >> k twoK := uint256(1) << k // 处理2^k部分 temp := u >> k rem := u & (twoK - 1) // 处理奇数m部分 tempDivM := (temp * inv25) >> 256 tempRemM := temp - tempDivM*uint256(m) // 合并最终结果 intFraction = uint(tempDivM) decFraction = uint((tempRemM << k) | rem) return }
说明
- 乘法逆元仅需预计算一次,无需在运行时计算,保证函数轻量级
- 该方案完全避免了字符串转换和通用除法的高开销,适合资源受限场景
内容的提问来源于stack exchange,提问作者JobWeek
相关产品推荐
相关产品推荐

