Насколько я понимаю, вы хотите выполнить (x*y)//z
.
Ваши числа x,y,z
все подходят на 64 бита, за исключением того, что вам нужно 128 бит для промежуточных x*y
.
Проблема действительно связана с разделением: у вас есть
h * y = qh * z + rh
l * y = ql * z + rl
h * y << 32 + l*y = (qh<<32 + ql) * z + (rh<<32 + rl)
, но ничего не говорит о (rh<<32 + rl) < z
, и в вашем случае высокие биты l*y
перекрывают младшие биты h * y
, поэтому вы получаете неправильное частное, отстоящее на потенциально много единиц.
То, что вы должны сделать в качестве второй операции, скорее:
rh<<32 + l * y = ql' * z + rl'
Затем получите общее частное qh<<32 + ql'
Но конечно, вы должны избегать переполнения при вычислении левого операнда ...
Поскольку вы разделяете только один из операндов x*y
, я предполагаю, что промежуточный результат всегда соответствует 96 битам.
Если это верно, то ваша задача состоит в том, чтобы разделить 3 32-битных конечности x*y
на 2 32-битных конечности z
.
Таким образом, это похоже на алгоритм Бернигеля-Циглера «разделяй и властвуй» для деления.
Алгоритм можно разложить так:
- получить 3 конечности
a2,a1,a0
умножения x*y
с помощью, например, карацубы - разделить z на 2 конечности
z1,z0
- выполнить
div32( (a2,a1,a0) , (z1,z0) )
вот какой-то псевдокод, имеющий дело только с положительными операндами и без гарантии правильности, но вы получаете представление о реализации:
p = 1<<32;
function (a1,a0) = split(a)
a1 = a >> 32;
a0 = a - (a1 * p);
function (a2,a1,a0) = mul22(x,y)
(x1,x0) = split(x) ;
(y1,y0) = split(y) ;
(h1,h0) = split(x1 * y1);
assert(h1 == 0); -- assume that results fits on 96 bits
(l1,l0) = split(x0 * y0);
(m1,m0) = split((x1 - x0) * (y0 - y1)); -- karatsuba trick
a0 = l0;
(carry,a1) = split( l1 + l0 + h0 + m0 );
a2 = l1 + m1 + h0 + carry;
function (q,r) = quorem(a,b)
q = a // b;
r = a - (b * q);
function (q1,q0,r0) = div21(a1,a0,b0)
(q1,r1) = quorem(a1,b0);
(q0,r0) = quorem( r1 * p + a0 , b0 );
(q1,q0) = split( q1 * p + q0 );
function q = div32(a2,a1,a0,b1,b0)
(q,r) = quorem(a2*p+a1,b1*p+b0);
q = q * p;
(a2,a1)=split(r);
if a2<b1
(q1,q0,r)=div21(a2,a1,b1);
assert(q1==0); -- since a2<b1...
else
q0=p-1;
r=(a2-b1)*p+a1+b1;
(d1,d0) = split(q0*b0);
r = (r-d1)*p + a0 - d0;
while(r < 0)
q = q - 1;
r = r + b1*p + b0;
function t=muldiv(x,y,z)
(a2,a1,a0) = mul22(x,y);
(z1,z0) = split(z);
if z1 == 0
(q2,q1,r1)=div21(a2,a1,z0);
assert(q2==0); -- otherwise result will not fit on 64 bits
t = q1*p + ( ( r1*p + a0 )//z0);
else
t = div32(a2,a1,a0,z1,z0);