我有两个数字,X和Y。
Y是单个无符号整数基元,例如long unsigned int
。 (在这种情况下,在执行操作之前,没有更大的基元可以向上转换。)
X由两个基元表示:X0与Y的类型相同,表示X的低位,X1是相同的类型,表示X的高位。
X / Y将始终使用与Y相同的类型表示,即可以假设操作不会溢出。 (因为X偶然是两个与Y相同类型的值的乘积,其中一个小于或等于Y.)
确定此划分结果的有效方法是什么?
答案 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 = 65454567423355465643444545
,Y = 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)
gcc
有__int128
和unsigned __int128
。我过去成功地使用它来执行您描述的这种操作。我相信所有主要编译器都有等价物。
答案 2 :(得分:0)
“将2位数字除以1位数,给出1位数的商和余数”是合成较大除法所需的基本原语。如果您的硬件中没有(数字== unsigned long int),则需要使用较小的数字。
在你的情况下,将Y分成2个半大小的整数,将X分成4个半大小的整数,并按此方式进行除法。