这个神奇的数字有什么作用?

逆向工程 拆卸 部件 x86 编译器优化
2021-06-28 23:53:00

我在 IDA 中打开了一个 64 位 DLL 文件。一个函数有这个伪代码:

unsigned __int64 output;
ULONG input;
output = (unsigned __int64)(input * (unsigned __int128)0xE38E38E38E38E38Fui64 >> 64) >> 5;

这是等效的装配视图:

mov     rcx, r13
mov     [rsp+56], rcx
mov     edx, [rsp+152]
mov     rax, 0E38E38E38E38E38Fh
mul     rdx
mov     r8, rdx
shr     r8, 5
mov     [r15], r8d
cmp     r8d, esi
cmova   r8d, ebx

当我想在 MSVC++ 中编译相同的代码时,它显示:

warning C4293: '>>': shift count negative or too big, undefined behavior

我的问题是,这个长常量值有什么作用?这个位移有什么神秘之处吗?

1个回答

尽管我将这个问题标记为重复,但由于使用了多个不同的幻数值,因此保留它具有一些附加值,将遇到不同幻值的用户重定向到同一资源。

在现代处理器上,整数除法是处理器可以执行的最慢的算术运算之一。通常比其他算术运算慢几个数量级。

事实证明,如果除数是常数,您可以使用巧妙的算术技巧来避免昂贵的除法运算,以进行一组相当快的运算(乘法、加法和移位),从而以稍大的代码为代价实现整体更快的性能。

这通常被称为“被幻数除法”,因为这些数字确实经常看起来像随机选择的数字,然而,情况并非如此。编译器(有时也有人)现在经常使用这些技巧,因为由于存储容量呈指数增长,代码的大小不再是一个问题。

一般的方法是我们可以开发如下等式:

X / Y == X * (1/Y) == sqrt((X * 2^n) * (1/Y)), 2^n) == ((x << n) * 1/Y)>>n

在您的情况下,0xE38E38E38E38E38Fui64位移3位是用于除以 9 的无符号 64 位整数的硬编码幻数。由于您的代码实际上移动了 5,因此实际除数等于9 * 2^2= 36