将由两个单词表示的数字除以由一个表示的数字?

时间:2017-02-20 17:09:11

标签: c math integer division

我有两个数字,X和Y。

Y是单个无符号整数基元,例如long unsigned int。 (在这种情况下,在执行操作之前,没有更大的基元可以向上转换。)

X由两个基元表示:X0与Y的类型相同,表示X的低位,X1是相同的类型,表示X的高位。

X / Y将始终使用与Y相同的类型表示,即可以假设操作不会溢出。 (因为X偶然是两个与Y相同类型的值的乘积,其中一个小于或等于Y.)

确定此划分结果的有效方法是什么?

3 个答案:

答案 0 :(得分:3)

您还没有指定平台,这对答案至关重要。

  

X / Y始终可以使用与Y相同的类型表示,即操作可以假设不溢出。 (因为X偶然是两个相同类型值的乘积   作为Y,其中一个小于或等于Y.)

在x86-64架构上,您可以通过划分RDX:RAX对来利用这一事实,因此它实际上与您拥有一个"粘合"相同。 128位寄存器用于分红。但要注意,如果上面不变量并不总是保持不变,那么你将从CPU获得除法异常

也就是说,一种实现是使用内联汇编,例如:

/* divides x1:x0 pair by y, assumes that quotient <= UINT64_MAX  */
uint64_t udiv128_64_unsafe(uint64_t x0, uint64_t x1, uint64_t y)
{
    __asm__ (
        "divq\t%3"
        : "=a" (x0)
        : "0" (x0), "d" (x1), "rm" (y)
    );
    return x0;
}

GCC 6.3.0很好地翻译(-O1):

udiv128_64_unsafe:
        mov     rcx, rdx            ; place the y (divisor) in RCX 
        mov     rax, rdi            ; low part of the dividend (x0)
        mov     rdx, rsi            ; high part of the divided (x1)
        divq    rcx                 ; RAX = RDX:RAX / RCX
        ret                         ; RAX is return value

例如,对于X = 65454567423355465643444545Y = 86439334393432232

#include <stdio.h>
#include <inttypes.h>

uint64_t udiv128_64_unsafe(uint64_t x0, uint64_t x1, uint64_t y) { ... }

int main(void)
{
    printf("%" PRIu64 "\n", udiv128_64_unsafe(0x35c0ecb3fea1c941ULL, 0x36248bULL,
        86439334393432232ULL));
    return 0;
}

给定的测试驱动程序产生:

757231275

答案 1 :(得分:0)

对于x86架构,

gcc__int128unsigned __int128。我过去成功地使用它来执行您描述的这种操作。我相信所有主要编译器都有等价物。

答案 2 :(得分:0)

“将2位数字除以1位数,给出1位数的商和余数”是合成较大除法所需的基本原语。如果您的硬件中没有(数字== unsigned long int),则需要使用较小的数字。

在你的情况下,将Y分成2个半大小的整数,将X分成4个半大小的整数,并按此方式进行除法。