相关疑难解决方法(0)

为什么GCC在实现整数除法时使用乘以奇数的乘法?

我一直在阅读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;
}
Run Code Online (Sandbox Code Playgroud)

然后生成汇编语言代码:

gcc -S division.c -O0 -masm=intel
Run Code Online (Sandbox Code Playgroud)

但是看生成的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 …
Run Code Online (Sandbox Code Playgroud)

c assembly gcc x86-64 integer-division

206
推荐指数
4
解决办法
1万
查看次数

82
推荐指数
7
解决办法
23万
查看次数

快速可分性测试(2,3,4,5,...,16)?

什么是最快的可分性测试?比如说,给定一个小端架构和一个32位有符号整数:如何计算得非常快,一个数字可被2,3,4,5整除,......最多16?

警告:给定的代码仅为示例.每一行都是独立的!使用模运算的明显解决方案在许多处理器上都很慢,这些处理器没有DIV硬件(像许多ARM一样).有些编译器也无法进行这样的优化(例如,如果divisor是函数的参数或依赖于某些东西).

Divisible_by_1 = do();
Divisible_by_2 = if (!(number & 1)) do();
Divisible_by_3 = ?
Divisible_by_4 = ?
Divisible_by_5 = ?
Divisible_by_6 = ?
Divisible_by_7 = ?
Divisible_by_8 = ?
Divisible_by_9 = ?
Divisible_by_10 = ?
Divisible_by_11 = ?
Divisible_by_12 = ?
Divisible_by_13 = ?
Divisible_by_14 = ?
Divisible_by_15 = ?
Divisible_by_16 = if(!number & 0x0000000F) do();
Run Code Online (Sandbox Code Playgroud)

和特殊情况:

Divisible_by_2k = if(number & (tk-1)) do();  //tk=2**k=(2*2*2*...) k times
Run Code Online (Sandbox Code Playgroud)

c c++ math assembly bit-manipulation

34
推荐指数
9
解决办法
2万
查看次数

为什么在 x86 上除以 3 需要右移(和其他奇怪的东西)?

我有以下 C/C++ 函数:

unsigned div3(unsigned x) {
    return x / 3;
}
Run Code Online (Sandbox Code Playgroud)

使用 clang 10 at编译-O3,结果为:

div3(unsigned int):
        mov     ecx, edi         # tmp = x
        mov     eax, 2863311531  # result = 3^-1
        imul    rax, rcx         # result *= tmp
        shr     rax, 33          # result >>= 33
        ret
Run Code Online (Sandbox Code Playgroud)

我所理解的是:除以 3 相当于乘以乘法逆 3 -1 mod 2 32,即 2863311531。

不过还是有些不明白的地方:

  1. 为什么我们需要使用ecx/rcx呢?我们不能乘raxedi直接?
  2. 为什么我们在 64 位模式下乘法?乘以eaxand不是更快ecx吗?
  3. 为什么我们使用imul …

c++ assembly compilation x86-64 integer-division

32
推荐指数
4
解决办法
1703
查看次数

C++快速除法/ mod乘10 ^ x

在我的程序中,我使用了很多整数除以10 ^ x和整数mod函数10.

例如:

unsigned __int64 a = 12345;
a = a / 100;
....
Run Code Online (Sandbox Code Playgroud)

要么:

unsigned __int64 a = 12345;
a = a % 1000;
....
Run Code Online (Sandbox Code Playgroud)

如果我要使用正确的位移>>,那么我将获得模式2^x,这不是我想要的.

有什么办法可以加速整数除法和mod函数的程序吗?

c++

22
推荐指数
5
解决办法
2万
查看次数

如何在没有printf的汇编级编程中从c库中打印整数?

任何人都可以告诉我纯粹的汇编代码以十进制格式显示寄存器中的值吗?请不要建议使用printf hack,然后使用gcc进行编译.

描述:

好吧,我做了一些研究和NASM的一些实验,并认为我可以使用c库中的printf函数来打印整数.我是通过使用GCC编译器编译目标文件来完成的,所有工作都很公平.

但是,我想要实现的是以十进制形式打印存储在任何寄存器中的值.

我做了一些研究,发现DOS命令行的中断向量021h可以显示字符串和字符,而2或9位于ah寄存器中,数据在dx中.

结论:

我找到的所有示例都没有显示如何在不使用C库的printf的情况下以十进制形式显示寄存器的内容值.有没有人知道如何在装配中这样做?

x86 assembly nasm cpu-registers

16
推荐指数
3
解决办法
3万
查看次数

有没有更有效的方法将数字分成数字?

我必须将数字拆分成数字才能在LCD上显示.现在我使用以下方法:

pos = 7;

do
{
    LCD_Display(pos, val % 10);
    val /= 10;
    pos--;
} while (pos >= 0 && val);
Run Code Online (Sandbox Code Playgroud)

这种方法的问题在于MSP430微控制器上的除法和模运算非常慢.有没有替代这种方法的东西,既不涉及分裂,也不会减少操作次数?

注意:我不能使用任何库函数,例如itoa.这些库很大,而且功能本身也非常耗费资源(在循环次数和RAM使用方面).

c performance digits

15
推荐指数
2
解决办法
1522
查看次数

GCC/ARM快速部门

据我所知,大多数编译器会通过乘法然后向右移位进行快速除法.例如,如果你检查这个SO线程,它会说当你要求Microsoft编译器除以10时,它会将被除数乘以0x1999999A(即2 ^ 32/10),然后将结果除以2 ^ 32(使用32向右移动).

到现在为止还挺好.

但是,一旦我使用GCC在ARM上测试了相同的除法,但编译器做了一些略微不同的事情.首先,它将被除数乘以0x66666667(2 ^ 34/10),然后将结果除以2 ^ 34.到目前为止,除了使用更高的乘数之外,它与Microsoft相同.然而,在那之后,它从结果中减去(被除数/ 2 ^ 31).

我的问题:为什么在ARM版本上有额外的减法?你能给我一个数字例子,如果没有减法,结果会出错吗?

如果你想检查生成的代码,它在下面(带我的评论):

        ldr     r2, [r7, #4] @--this loads the dividend from memory into r2
        movw    r3, #:lower16:1717986919 @--moves the lower 16 bits of the constant 
        movt    r3, #:upper16:1717986919 @--moves the upper 16 bits of the constant
        smull   r1, r3, r3, r2 @--multiply long, put lower 32 bits in r1, higher 32 in r3
        asr     r1, r3, #2 @--r3>>2, then store in r1 (effectively …
Run Code Online (Sandbox Code Playgroud)

assembly gcc arm integer-division

10
推荐指数
2
解决办法
3495
查看次数

当 x 的范围有限时,为什么 x/10 会通过不必要的偏移进行优化?

我有这个功能

long long int divideBy10(long long int a){
    return a / 10;
}
Run Code Online (Sandbox Code Playgroud)

它被编译为:

        mov     rax, rdi
        movabs  rcx, 7378697629483820647
        imul    rcx
        mov     rax, rdx
        shr     rax, 63
        sar     rdx, 2
        add     rax, rdx
        ret
Run Code Online (Sandbox Code Playgroud)

如果我添加 __builtin_assume(a > 0);

它被编译为

        mov     rax, rdi
        movabs  rcx, -3689348814741910323
        mul     rcx
        mov     rax, rdx
        shr     rax, 3
        ret
Run Code Online (Sandbox Code Playgroud)

代码效率更高,因为它不必担心负号。现在,如果我添加 __builtin_assume(a < 10000); 我原以为它会被编译成一个乘法而没有移位。但事实并非如此。

我想也许编译器只跟踪数字是正数还是负数,但是

long long int noBranch(long long int a){
    __builtin_assume(a < 400);
    if( a < 500){
        return a;
    }
    return 0; …
Run Code Online (Sandbox Code Playgroud)

c++ compiler-construction assembly compiler-optimization

9
推荐指数
0
解决办法
206
查看次数

如何使用shift/add/sub除以9?

上周我接受了采访,有一个这样的测试:

使用SHIFT LEFT,SHIFT RIGHT,ADD,SUBSTRACT指令计算N/9(给定N为正整数) .

algorithm assembly cpu-architecture integer-arithmetic

8
推荐指数
1
解决办法
1106
查看次数