当前位置: 首页 > 知识库问答 >
问题:

为什么GCC在实现整数除法时会使用乘以一个奇怪的数字?

东郭瀚玥
2023-03-14

我一直在读divmul汇编操作,我决定用C:

#include <stdlib.h>
#include <stdio.h>

int main()
{
    size_t i = 9;
    size_t j = i / 5;
    printf("%zu\n",j);
    return 0;
}
gcc -S division.c -O0 -masm=intel

但是查看生成的division.s文件,它不包含任何div操作!取而代之的是,它使用比特移动和魔法数字来执行某种黑魔法。下面是计算I/5的代码段:

mov     rax, QWORD PTR [rbp-16]   ; Move i (=9) to RAX
movabs  rdx, -3689348814741910323 ; Move some magic number to RDX (?)
mul     rdx                       ; Multiply 9 by magic number
mov     rax, rdx                  ; Take only the upper 64 bits of the result
shr     rax, 2                    ; Shift these bits 2 places to the right (?)
mov     QWORD PTR [rbp-8], rax    ; Magically, RAX contains 9/5=1 now, 
                                  ; so we can assign it to j

这是怎么回事?为什么GCC根本不使用div?它是如何产生这个神奇的数字的,为什么一切都起作用?

共有1个答案

辛弘壮
2023-03-14

整数除法是在现代处理器上可以执行的最慢的算术运算之一,延迟可达几十个周期,吞吐量也很差。(对于x86,请参阅Agner Fog的说明表和微拱形指南)。

如果您提前知道除数,那么您可以通过用一组具有同等作用的其他操作(乘法、加法和移位)替换它来避免除法。即使需要几次运算,它仍然比整数除法本身要快得多。

以这种方式实现C/运算符,而不是使用包含div的多指令序列,这正是GCC按常量除法的默认方式。它不需要跨操作进行优化,即使是调试也不会更改任何内容。(但是,对于较小的代码大小使用-os会使GCC使用div)使用乘法逆代替除法就像使用lea代替muladd

因此,只有在编译时不知道除数的情况下,您才倾向于在输出中看到dividiv

有关编译器如何生成这些序列的信息,以及允许您为自己生成这些序列的代码(除非使用Brainead编译器,否则几乎肯定是不必要的),请参见libdivide。

 类似资料:
  • 我正在使用最新版本的ARM打包GCC: arm none eabi gcc(GNU arm Embedded Toolchain 10-2020-q4-major)10.2.1 20201103(发布)版权所有(C)2020自由软件基金会。 当我使用“-mcpu=cortex-m0-mthumb-Ofast”编译这段代码时: 我希望除法是通过乘法和移位来完成的,但相反,生成了以下代码: 使用_ud

  • 题目描述 Java 数组扩容问题:实现动态的给数组添加元素效果,实现对数组扩容 原始数组 int[] arr = {1,2,3} 增加的元素 4,直接放在数组的最后 arr = {1,2,3,4} 题目来源及自己的思路 定义 arr1 定义 arr2,比 arr1 的长度长 1 在 arr1 的长度内,把 arr1 的值赋值给 arr2 arr2 的最后一个位置赋值为 4,也就是要加入的数据 因为

  • 问题内容: 在回答了这个问题之后,我很困惑为什么这段代码中的整数溢出而不是负数。奇怪,为什么这么精确的数字?为什么是0? 输出: 问题答案: 仅当的起始值为偶数时,才会发生这种情况。 根据JLS§15.17.1: 如果整数乘法溢出,则结果是数学乘积 的低阶位 ,以某种足够大的二进制补码格式表示。结果,如果发生溢出,则结果的符号可能与两个操作数值的数学积的符号不同。 如果我们以二进制格式而不是十进制

  • 问题内容: 这是代码片段: 输出为: 为什么会这样呢?我认为是,要么,或。 这里发生了什么? 问题答案: 二是算术加法,不是字符串连接。您必须执行或之类的操作,或使用和方法来确保操作符中的至少一个是用于字符串串联的运算符。 [JLS 15.18加法运算符](http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#

  • 问题内容: 我输入要获取的销售金额(按输入)乘以定义的营业税(0.08),然后打印总金额(营业税乘以销售金额)。 我碰到这个错误。有人知道什么地方可能有问题或有什么建议吗? 问题答案: 返回一个字符串(一个字符序列)。在Python中,将字符串和浮点数相乘并没有定义的含义(而将字符串和整数相乘则具有以下含义:is ;多少是多少?请不要回复)。您需要将字符串解析为数字值。 您可能要尝试:

  • 问题内容: 因此,如果我有一个范围为‘0-1024’的数字,并且希望将其取为‘0-255’,那么数学将决定将输入除以输入的最大值(在这种情况下为1024),从而得出我的数字介于0.0-1.0之间。然后将其乘以目标范围(255)。 我要做什么! 但是由于Java中的某些原因(使用处理),它将始终返回值0。 该代码将像这样简单 但是我只得到0.0。我已经尝试过两次和诠释。一切都无济于事。为什么!? 问