Skip to content

Commit b6e4387

Browse files
committed
Improve integer division for small denominators
1 parent 9f041d0 commit b6e4387

File tree

1 file changed

+40
-34
lines changed

1 file changed

+40
-34
lines changed

bint.lua

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -394,20 +394,6 @@ do
394394
end
395395
end
396396

397-
-- Single word division modulus
398-
local function sudivmod(nume, deno)
399-
local quot = bint_newempty()
400-
local rema
401-
local carry = 0
402-
for i=BINT_SIZE,1,-1 do
403-
carry = carry | nume[i]
404-
quot[i] = carry // deno
405-
rema = carry % deno
406-
carry = rema << BINT_WORDBITS
407-
end
408-
return quot, rema
409-
end
410-
411397
--- Convert a bint to a string in the desired base.
412398
-- @param x The bint to be converted from.
413399
-- @param[opt] base Base to be represented, defaults to 10.
@@ -446,9 +432,11 @@ function bint.tobase(x, base, unsigned)
446432
local neg = not unsigned and x:isneg()
447433
if neg then
448434
x = x:abs()
435+
else
436+
x = bint.new(x)
449437
end
450-
local stop = x:iszero()
451-
if stop then
438+
local xiszero = x:iszero()
439+
if xiszero then
452440
return '0'
453441
end
454442
-- calculate basepow
@@ -459,21 +447,33 @@ function bint.tobase(x, base, unsigned)
459447
step = step + 1
460448
basepow = basepow * base
461449
until basepow >= limit
462-
-- spit out base digits
463-
while not stop do
464-
local xd
465-
x, xd = sudivmod(x, basepow)
466-
stop = x:iszero()
450+
-- serialize base digits
451+
local size = BINT_SIZE
452+
local xd, carry, d
453+
repeat
454+
-- single word division
455+
carry = 0
456+
xiszero = true
457+
for i=size,1,-1 do
458+
carry = carry | x[i]
459+
d, xd = carry // basepow, carry % basepow
460+
if xiszero and d ~= 0 then
461+
size = i
462+
xiszero = false
463+
end
464+
x[i] = d
465+
carry = xd << BINT_WORDBITS
466+
end
467+
-- digit division
467468
for _=1,step do
468-
local d
469469
xd, d = xd // base, xd % base
470-
if stop and xd == 0 and d == 0 then
470+
if xiszero and xd == 0 and d == 0 then
471471
-- stop on leading zeros
472472
break
473473
end
474474
table.insert(ss, 1, BASE_LETTERS[d])
475475
end
476-
end
476+
until xiszero
477477
if neg then
478478
table.insert(ss, 1, '-')
479479
end
@@ -1063,6 +1063,19 @@ local function findleftbit(x)
10631063
end
10641064
end
10651065

1066+
-- Single word division modulus
1067+
local function sudivmod(nume, deno)
1068+
local rema
1069+
local carry = 0
1070+
for i=BINT_SIZE,1,-1 do
1071+
carry = carry | nume[i]
1072+
nume[i] = carry // deno
1073+
rema = carry % deno
1074+
carry = rema << BINT_WORDBITS
1075+
end
1076+
return rema
1077+
end
1078+
10661079
--- Perform unsigned division and modulo operation between two integers considering bints.
10671080
-- This is effectively the same of @{bint.udiv} and @{bint.umod}.
10681081
-- @param x The numerator, must be a bint or a lua integer.
@@ -1077,7 +1090,7 @@ function bint.udivmod(x, y)
10771090
local deno = bint_assert_convert(y)
10781091
-- compute if high bits of denominator are all zeros
10791092
local ishighzero = true
1080-
for i=BINT_SIZE,2,-1 do
1093+
for i=2,BINT_SIZE do
10811094
if deno[i] ~= 0 then
10821095
ishighzero = false
10831096
break
@@ -1092,9 +1105,8 @@ function bint.udivmod(x, y)
10921105
return nume, bint.zero()
10931106
elseif low <= (BINT_WORDMSB - 1) then
10941107
-- can do single word division
1095-
local rema
1096-
nume, rema = sudivmod(nume, low)
1097-
return nume, bint.new(rema)
1108+
local rema = sudivmod(nume, low)
1109+
return nume, bint.fromuinteger(rema)
10981110
end
10991111
end
11001112
if nume:ult(deno) then
@@ -1188,9 +1200,6 @@ end
11881200
function bint.idivmod(x, y)
11891201
local ix, iy = bint.tobint(x), bint.tobint(y)
11901202
if ix and iy then
1191-
if iy:isminusone() then
1192-
return -ix, bint.zero()
1193-
end
11941203
local quot, rema = bint.udivmod(ix:abs(), iy:abs())
11951204
local isnumneg, isdenomneg = ix:isneg(), iy:isneg()
11961205
if isnumneg ~= isdenomneg then
@@ -1226,9 +1235,6 @@ end
12261235
function bint.__idiv(x, y)
12271236
local ix, iy = bint.tobint(x), bint.tobint(y)
12281237
if ix and iy then
1229-
if iy:isminusone() then
1230-
return -ix, bint.zero()
1231-
end
12321238
local quot, rema = bint.udivmod(ix:abs(), iy:abs())
12331239
if ix:isneg() ~= iy:isneg() then
12341240
quot:_unm()

0 commit comments

Comments
 (0)