JS除法转乘法魔数计算代码中循环结构的作用与实现原理
整数除法魔数实现差异疑问
我从Hacker's Delight的存档页面获取到如下JS实现代码,该代码接收除数(例如7)即可输出对应乘法运算使用的魔数,后续配合位移操作就能得到整数除法的计算结果。
我不熟悉汇编及相关数论原理,按照我的理解,魔数可以通过公式ceil(1/divisor * 1<<32)计算(64位运算场景下使用<<64,需要更大位宽的int类型支持):使用imul指令做整数乘法时,乘积高位和余数会分别存储在不同寄存器中,取我推导公式算出的魔数与被除数相乘后的高位寄存器值,就能得到正确的整数除法结果。我编写了如下C++测试代码验证了部分测试用例,运行结果均符合预期:
#include <cstdio> #include <cassert> int main(int argc, char *argv[]) { auto test_divisor = 7; auto test_value = 43; auto a = test_value*test_divisor; auto b = a-1; //One less test auto magic = (1ULL<<32)/test_divisor; if (((1ULL<<32)%test_divisor) != 0) { magic++; //Round up } auto answer1 = (a*magic) >> 32; auto answer2 = (b*magic) >> 32; assert(answer1 == test_value); assert(answer2 == test_value-1); printf("%lld %lld\n", answer1, answer2); }
但取自Hacker's Delight的JS魔数计算实现却包含循环等额外逻辑,我有如下疑问:
- 该循环的设计作用是什么?
- 我的无循环实现存在什么疏漏?
- 存在哪些测试用例会让我的实现计算错误,而该JS实现可以返回正确结果?
对应的JS与HTML代码如下:
var two31 = 0x80000000 var two32 = 0x100000000 function magic_signed(d) { with(Math) { if (d >= two31) d = d - two32// Treat large positive as short for negative. var ad = abs(d) var t = two31 + (d >>> 31) var anc = t - 1 - t%ad // Absolute value of nc. var p = 31 // Init p. var q1 = floor(two31/anc) // Init q1 = 2**p/|nc|. var r1 = two31 - q1*anc // Init r1 = rem(2**p, |nc|). var q2 = floor(two31/ad) // Init q2 = 2**p/|d|. var r2 = two31 - q2*ad // Init r2 = rem(2**p, |d|). do { p = p + 1; q1 = 2*q1; // Update q1 = 2**p/|nc|. r1 = 2*r1; // Update r1 = rem(2**p, |nc|. if (r1 >= anc) { // (Must be an unsigned q1 = q1 + 1; // comparison here). r1 = r1 - anc;} q2 = 2*q2; // Update q2 = 2**p/|d|. r2 = 2*r2; // Update r2 = rem(2**p, |d|. if (r2 >= ad) { // (Must be an unsigned q2 = q2 + 1; // comparison here). r2 = r2 - ad;} var delta = ad - r2; } while (q1 < delta || (q1 == delta && r1 == 0)) var mag = q2 + 1 if (d < 0) mag = two32 - mag // Magic number and shift = p - 32 // shift amount to return. return mag }} byId('subBtn').onclick = function (e) { e.preventDefault(); inputVal = byId('value').value + 0; byId('result').innerText = "" + magic_signed(inputVal); } function byId(x) { return document.getElementById(x); }
<h1>Hacker's Delight Magic Number</h1> <form name="magic_number">Value: <input id="value"> <button id='subBtn'>Calculate</button><br> <label id="result"></label> </form>
内容的提问来源于stack exchange,提问作者Cal
相关产品推荐
相关产品推荐

