You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带符号数的2的幂次除法与取模运算探究及实现解析

负数对2的幂次的整数除法与取模(符合C++标准的SSE实现)

核心规则回顾

C++标准明确:

  • 整数除法向零取整(如 -5/4 = -1,-3/4 = 0)
  • 取模运算满足等式 X = (X/Y)*Y + X%Y,余数符号与被除数X一致(如 -5%4 = -1,-4%4 = 0)

正整数场景下的X/Y == X>>K、X%Y == X&(Y-1)公式对负数不适用,因为x86架构的算术右移是向负无穷取整,与C++的向零取整规则冲突,必须做针对性调整。

负数场景的通用公式

设除数 Y=2^K(K为正整数,编译期未知),X为有符号整数:

1. 整数除法(向零取整)

X / Y = (X + ((X >> (bit_width-1)) & (Y-1))) >> K
  • X >> (bit_width-1):提取X的符号位,负数时结果为全1(如32位int是0xffffffff),正数时为全0
  • ((X >> ...) & (Y-1)):负数时得到Y-1,正数时得到0
  • 本质:给负数补Y-1后再右移,抵消算术右移向负无穷取整的偏差,实现向零取整

2. 取模运算

基于除法结果推导,直接满足C++的等式规则:

X % Y = X - (X/Y)*Y

也可通过位运算优化(需判断符号),但通用场景下直接用减法更稳妥。

Clang x86-64汇编代码解释

以32位int为例,Clang编译的除法/取模函数汇编逻辑完全对应上述公式:

除法函数汇编

div_pow2:
        movl    %edi, %eax          # 加载被除数X到eax
        movl    $1, %ecx
        shll    %cl, %ecx          # ecx = Y = 1 << K
        leal    -1(%rcx), %edx     # edx = Y-1
        sarl    $31, %eax          # eax = X >> 31(符号位扩展)
        andl    %edx, %eax         # eax = (X>>31) & (Y-1)
        addl    %edi, %eax         # eax = X + 上述修正值
        sarl    %cl, %eax          # eax = 修正后的值右移K位,得到向零取整的商
        ret

取模函数汇编

mod_pow2:
        movl    %edi, %eax
        movl    $1, %ecx
        shll    %cl, %ecx          # ecx = Y
        leal    -1(%rcx), %edx
        sarl    $31, %eax
        andl    %edx, %eax
        addl    %edi, %eax
        sarl    %cl, %eax          # 计算得到商q
        imull   %ecx, %eax         # eax = q*Y
        subl    %eax, %edi         # edi = X - q*Y,得到余数
        movl    %edi, %eax
        ret

SSE实现思路

针对__m128i向量(以32位int元素为例),利用SSE指令集实现批量运算:

除法实现

#include <emmintrin.h>

__m128i sse_div_pow2(__m128i x, int k) {
    const __m128i y = _mm_set1_epi32(1 << k);
    const __m128i y_minus_1 = _mm_sub_epi32(y, _mm_set1_epi32(1));
    // 提取符号位:负数为全1,正数为全0
    __m128i sign_mask = _mm_srai_epi32(x, 31);
    // 计算修正值:负数取Y-1,正数取0
    __m128i adjust = _mm_and_si128(sign_mask, y_minus_1);
    // 修正后右移K位
    __m128i corrected_x = _mm_add_epi32(x, adjust);
    return _mm_srai_epi32(corrected_x, k);
}

取模实现

__m128i sse_mod_pow2(__m128i x, int k) {
    __m128i q = sse_div_pow2(x, k);
    const __m128i y = _mm_set1_epi32(1 << k);
    __m128i q_mul_y = _mm_mullo_epi32(q, y);
    return _mm_sub_epi32(x, q_mul_y);
}

内容的提问来源于stack exchange,提问作者M.kazem Akhgary

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 19:57:34