Loading [MathJax]/extensions/Safe.js


Post Reply 
(42, all flavours) Integer Division - how?
12-14-2020, 05:16 AM
Post: #21
RE: (42, all flavours) Integer Division - how?
(12-13-2020 06:41 PM)Albert Chan Wrote:  It would be nice if Free42 exposed FMA(a,b,c) to the user ...

An alternative to FMA is an accurate cross product: ab - cd (or equally ab + cd). This can be used for a number of things including FMA.


Pauli
Find all posts by this user
Quote this message in a reply
12-14-2020, 06:59 AM (This post was last modified: 12-14-2020 07:59 AM by Werner.)
Post: #22
RE: (42, all flavours) Integer Division - how?
(corrected)
using floor(q) now, and the 'correct or not' approach:
(replace all NOPs with LBL 00 on a real 42S, or the DM42 for the time being)

Code:
 { 45-Byte Prgm } @     X       Y       Z       T
 LBL "DIV" @            b       a
 RCL ST Y
 RCL ST Y
 /
 IP @                  q        b       a       a
 LASTX
 X<Y?
 DSE ST Y
 NOP @                        floor(q)  b       a
 R^
 STO ST Y @            a        a       q       b
 R^
 MOD @          b      a%b      a       q       q
 ISG ST L @     c
 NOP
 X<>Y
 LASTX @                c       a       a%b     q 
 MOD
 STO- ST Y @    c       a%c     a'      q       q
 X<> ST T @     c       q       a'      q
 LASTX @                c       q       a'      q
 MOD @          c       b'      a'      q       q
 X#Y?
 RCL- ST L
 X#Y?
 DSE ST T
 NOP
 R^
 END

Cheers, Werner

41CV†,42S,48GX,49G,DM42,DM41X,17BII,15CE,DM15L,12C,16CE
Find all posts by this user
Quote this message in a reply
12-14-2020, 07:01 AM
Post: #23
RE: (42, all flavours) Integer Division - how?
(12-14-2020 05:16 AM)Paul Dale Wrote:  An alternative to FMA is an accurate cross product: ab - cd (or equally ab + cd). This can be used for a number of things including FMA.

Yes, but an FMA is built into the Intel library, and extended precision is not..
Werner

41CV†,42S,48GX,49G,DM42,DM41X,17BII,15CE,DM15L,12C,16CE
Find all posts by this user
Quote this message in a reply
12-14-2020, 08:04 AM (This post was last modified: 12-14-2020 08:08 AM by Albert Chan.)
Post: #24
RE: (42, all flavours) Integer Division - how?
(12-14-2020 06:59 AM)Werner Wrote:  using floor(q) now, and the 'correct or not' approach:

I started with q = floor(a/b), then correct, but it is not necessary.
You can use round(a/b), ceil(a/b), IP(a/b), ... it does not matter.

The correction, r is still limited to 0, -1

Example, say we use ceil(a/b) for initial q

a/b = 10 - ε        → q = 10      → r = -1
a/b = 10 +ε        → q = 11      → r = -1

What if a/b = 10 ± ε, because of round-to-nearest, get rounded to exactly 10 ?
It is OK. For interger, round/ceil/floor/IP does nothing to it.

Since edge case work, so is the rest ...
Find all posts by this user
Quote this message in a reply
12-14-2020, 09:10 AM
Post: #25
RE: (42, all flavours) Integer Division - how?
That simplifies things:

Code:
 { 37-Byte Prgm } @     X       Y       Z       T
 LBL "DIV" @            b       a
 RCL ST Y
 RCL ST Y
 /
 IP @                  q        b       a       a
 Rv
 MOD @          b      a%b      a       q       q
 ISG ST L @     c
 NOP
 X<>Y
 LASTX @                c       a       a%b     q 
 MOD
 STO- ST Y @    c       a%c     a'      q       q
 X<> ST T @     c       q       a'      q
 LASTX @                c       q       a'      q
 MOD @          c       b'      a'      q       q
 X#Y?
 RCL- ST L
 X#Y?
 DSE ST T
 NOP
 R^
 END

Werner

41CV†,42S,48GX,49G,DM42,DM41X,17BII,15CE,DM15L,12C,16CE
Find all posts by this user
Quote this message in a reply
12-14-2020, 10:10 AM
Post: #26
RE: (42, all flavours) Integer Division - how?
(12-13-2020 06:41 PM)Albert Chan Wrote:  Bonus: it removed the integer arguments requirement.

At the risk of being proven wrong again..
as long as b+1 is exact, (and q is not too large), the code for idiv3 works for fractional a and b, too, no?

function idiv3(a,b) -- assumed b > 0
local q, c = floor(a/b), b+1
a, b = a%b - a%c, q%c -- a-b = [2-2c, c-2]
if a==b or a==b-c then return q end
return q-1
end

Werner

41CV†,42S,48GX,49G,DM42,DM41X,17BII,15CE,DM15L,12C,16CE
Find all posts by this user
Quote this message in a reply
12-14-2020, 04:12 PM
Post: #27
RE: (42, all flavours) Integer Division - how?
(12-14-2020 10:10 AM)Werner Wrote:  At the risk of being proven wrong again..
as long as b+1 is exact, (and q is not too large), the code for idiv3 works for fractional a and b, too, no?

function idiv3(a,b) -- assumed b > 0
local q, c = floor(a/b), b+1
a, b = a%b - a%c, q%c -- a-b = [2-2c, c-2]
if a==b or a==b-c then return q end
return q-1
end

Yes, fractions still work! Big Grin

a = (a//b)*b + (a%b) = q*(c-1) + (a%b)

  → a%b - a%c - q%c ≡ 0 (mod c)

If c=b+1 is exact, and q is correct, above identity holds.

And, optimization for (mod c) by 2 comparisons also hold.
Checking range of each terms (note, I use opened end intervals here):

a1 = (a%b) - (a%c) = [0, c-1) + (-c, 0] = (-c, c-1)
b1 = (q%c) = [0, c)

a1 - b1 = (-c,c-1) + (-c,0] = (-2c, c-1)

a1 - b1 ≡ 0 (mod c)     → a1 - b1 = 0, or -c

But, a1 - b1 = -c implied we need room, such that c+ulp(c) ≠ c
So, we switched it around, and test: (a1==b1) or (a1==b1-c)

---

c=b+1 (exactly) is equivalent to test: (b+1-1-b == 0) and (b+1-b-1 == 0)
With b>0, ulp(b) = ulp(c) = some multiples of machine epsilon.

This is lua 5.4 implmentation for floor-mod, from llimits.h
PHP Code:
#define luai_nummod(L,a,b,m)  \
  
{ (void)L; (m) = l_mathop(fmod)(a,b); \
    if (((
m) > 0) ? (b) < : ((m) < && (b) > 0)) (m) += (b); }
#endif 

For our situation, m and b have the same sized ulp.
Even if it needed sign correction, (m) += (b), there no no cancellation errors.

ULP consideration is needed. Unlike truncated fmod, floor-mod might not be exact.
Eli Bendersky's post showed why fmod is exact: Computing Remainders by Doubling

We need to watch out for cancellation errors, when floor-mod fix the sign.

lua> eps = 1e-10
lua> x, y = eps%1, -eps%1
lua> x, y
1e-10      0.9999999999
lua> x+y, 1-y-x                -- x+y rounded to 1, but not exactly equal 1
1            8.274037096265818e-018
Find all posts by this user
Quote this message in a reply
12-14-2020, 06:01 PM
Post: #28
RE: (42, all flavours) Integer Division - how?
(12-14-2020 04:12 PM)Albert Chan Wrote:  a = (a//b)*b + (a%b) = q*(c-1) + (a%b)

  → a%b - a%c - q%c ≡ 0 (mod c)

If c=b+1 is exact, and q is correct, above identity holds.

I forget to show what happened if q is not correct.
In other words, if q is correct, show:

a%b - a%c - (q±1)%c ≠ 0 (mod c)

Assumed what is to proof is actually true, subtract, we get:

±1 ≠ 0 (mod c)

Since b > 0, c = b+1 > 1, this is always true ⇒ assumption is true too.
Find all posts by this user
Quote this message in a reply
12-15-2020, 10:37 AM
Post: #29
RE: (42, all flavours) Integer Division - how?
(12-14-2020 04:12 PM)Albert Chan Wrote:  ULP consideration is needed. Unlike truncated fmod, floor-mod might not be exact.

For idiv3(a, b), with tiny negative a, indeed we may hit with cancellation bug.

lua> a = -0.09
lua> b = 1.1 - 1 -- so c = b+1 is exact
lua> idiv3(a , b) -- should be -1
-2

For negative tiny a = -ε

a1 = a%b - a%c = (b-ε) - (c-ε) = -1 + error
b1 = q%c = q = 0, or -1

It is this error that make idiv3 failed.

If the problem scaled to integers, the problem goes away.

lua> A, B = a*0x1p56, b*0x1p56
lua> C = B+1
lua> A%B - A%C
-1
lua> idiv3(A,B)
-1
Find all posts by this user
Quote this message in a reply
12-15-2020, 12:43 PM
Post: #30
RE: (42, all flavours) Integer Division - how?
Yes, but that is only with binary floating point, not with decimal, as in HP calcs ;-)
Werner

41CV†,42S,48GX,49G,DM42,DM41X,17BII,15CE,DM15L,12C,16CE
Find all posts by this user
Quote this message in a reply
12-15-2020, 02:51 PM (This post was last modified: 12-16-2020 08:30 PM by Albert Chan.)
Post: #31
RE: (42, all flavours) Integer Division - how?
(12-15-2020 12:43 PM)Werner Wrote:  Yes, but that is only with binary floating point, not with decimal, as in HP calcs ;-)

Not true. When floor-mod "fix" sign, subtraction cancellation may occur, binary or decimal.

But, the fix is easy. (your code already fixed this, by using IP)

The trick is to start with q = ceil(a/b), or IP(a/b)

For a<0, |a|<b: min(a/b) = min(ulp(b)-b)/b) = min(ulp(b)/b) - 1 > -1

Confirm:
Free42 Binary: 2 53 X^Y 1 - 1/X 1 -  → -9.999999999999999e-1
Free42 Decimal: 1E34 1 - 1/X 1 -      → -9.999999999999999999999999999999999e-1

This guaranteed q start with 0, for negative tiny a.

(12-15-2020 10:37 AM)Albert Chan Wrote:  For negative tiny a = -ε

a1 = a%b - a%c = (b-ε) - (c-ε) = -1 + error
b1 = q%c = q = 0, or -1

It is this error that make idiv3 failed.

If error = 0, this always work, nothing to fix.
If error ≠ 0, and q = 0, we get this:

a1 == b1 test : -1 + error == 0     → error == 1
a1==b1-c test: -1 + error == -c    → error == -b

Since error is tiny, error(b-ε) ≤ ½ulp(b), error(c-ε) ≤ ½ulp(c), both tests failed.
This force correction, returning corrected q-1 = -1

This is revised idiv3 should work with non-integer inputs, if c = b+1 is exact.
I also added a fast-path, to speed things up.

PHP Code:
function idiv3c(a,b)        -- assumed b 0
    local q
ceil(a/b), b+1
    
if q*a then return q-1 end
    a
a%a%cq%c   -- a-= (-2cc-1)
    if 
a==or a==b-c then return q end
    
return q-1
end 
Find all posts by this user
Quote this message in a reply
12-16-2020, 03:30 PM (This post was last modified: 12-17-2020 12:35 AM by Albert Chan.)
Post: #32
RE: (42, all flavours) Integer Division - how?
(12-13-2020 07:43 PM)Thomas Okken Wrote:  
(12-13-2020 06:41 PM)Albert Chan Wrote:  It would be nice if Free42 exposed FMA(a,b,c) to the user ...

Noted...

Free42 FMA already work in progress ! Smile
https://github.com/thomasokken/free42

With FMA, idiv simplified to this. (code optimized for speed, and removed b>0 requirement)
PHP Code:
function idiv5(a,b)
    
local c a/b
    local q 
floor(c)
    if 
0 then b=-else a=-a end
    
if == and fma(q,b,a) < 0 then return q-1 end
    
return q
end 
Find all posts by this user
Quote this message in a reply
12-17-2020, 08:53 AM
Post: #33
RE: (42, all flavours) Integer Division - how?
(12-15-2020 02:51 PM)Albert Chan Wrote:  I also added a fast-path, to speed things up.

PHP Code:
function idiv3c(a,b)        -- assumed b 0
    local q
ceil(a/b), b+1
    
if q*a then return q-1 end
    a
a%a%cq%c   -- a-= (-2cc-1)
    if 
a==or a==b-c then return q end
    
return q-1
end 

That fast path wouldn't be useful in my case: I wanted to use integer division to correctly split
Cc00 = Qq*Xx + Rr, where each letter is a half-length integer and Cc<Xx

For instance, in a 2-digit calculator:
2300 = 57*40 + 20, but 2300/40=58
2700 = 37*72 + 36

What I did up till now is determine Q and q separately with
Cc0 = Q*Xx + Aa
Aa0 = q*Xx + Rr

and each split Yy0=Q*Xx+Rr is carried out as
Q := Yy0/Xx;
Rr := MOD(Yy0,Xx);
if FRC(Q) >0
then Q := INT(Q);
else
if Rr>0 then
Q := ROUND(Q - Rr/Xx,0);
end;
end;

sadly, that still is faster than the integer division - mainly because the rounding part is almost never reached, certainly not in the 34-digit Free42..

Werner

41CV†,42S,48GX,49G,DM42,DM41X,17BII,15CE,DM15L,12C,16CE
Find all posts by this user
Quote this message in a reply
12-17-2020, 10:20 AM (This post was last modified: 12-17-2020 10:50 AM by Werner.)
Post: #34
RE: (42, all flavours) Integer Division - how?
the rounding part I do as

if R>X-R then Q:= Q-1;

for inputs Cc0 and Xx, and Cc<Xx, if FP(Cc0/Xx)=0, R # X-R (round-to-even does not occur, while in Cc00/Xx it does)

Werner

41CV†,42S,48GX,49G,DM42,DM41X,17BII,15CE,DM15L,12C,16CE
Find all posts by this user
Quote this message in a reply
12-17-2020, 12:42 PM (This post was last modified: 12-17-2020 07:28 PM by Werner.)
Post: #35
RE: (42, all flavours) Integer Division - how?
Actually that code works for Cc00 = Qq*Xx + Rr, too, save for the round-to-even cases, that are very rare.
So I can do

Code:
Qq := Cc00/Xx;
Rr := MOD(Cc00,Xx);
if FRC(Qq) >0
then Qq := INT(Qq);
else
  if Rr>0 then
    if Rr>Xx-Rr then Qq:= Qq - 1;
    elsif Rr = Xx-Rr then
    c := Xx+1;
    b := MOD(Qq,c);
    a := Rr - MOD(CC00,c);
    if NOT(a=b or a=b-c ) then Qq:=Qq - 1;
  end;
end;

and the test for Rr>0 is not necessary either

Werner

41CV†,42S,48GX,49G,DM42,DM41X,17BII,15CE,DM15L,12C,16CE
Find all posts by this user
Quote this message in a reply
12-17-2020, 06:41 PM
Post: #36
RE: (42, all flavours) Integer Division - how?
(12-17-2020 12:42 PM)Werner Wrote:  Actually that code works for Cc00 = Qq*Xx + Rr, too, save for the round-to-even cases, that are very rare.
Code:
    Half-way-case correction ...
    elsif Rr = Xx-Rr then
    c := Xx+1;
    b := INT(Cc00/c);
    a := Rr - MOD(CC00,c);
    if NOT(a=b or a=b-c ) then Qq:=Qq - 1;
  end;
end;

Your half-way-case had a typo. It should be b := Qq % c
If Qq is correct, we have:

Cc00 = Qq*Xx + Rr = Qq*c - Qq + Rr
Qq = Qq*c + Rr - Cc00

We take (mod c) for both side, b = LHS, a = RHS, and do the test.

---

If remainder() is available, we can replace (mod c) test with it.
PHP Code:
function remainder(xy)            -- assumed x >=00
    local r
halfy fmod(xy+y), y/2
    
if <= halfy then return r end -- = (x-r)/y must be even
    r 
y                       -- q is odd= (-y/2y)
    return 
halfy and or -- halfway case: = -y/2q is even
end 

remainder() guaranteed that halfway case have even quotient.

For half-way case, Qq must be even (example, 11.5 -> 12, 12.5 -> 12)
We can use sign of remainder, for the half-way case correction:

→ if remaider(Cc00, Xx) < 0 then Qq := Qq - 1;

---

If Cc00/Xx fractional part is 0.5, it implied Cc000 / Xx is integer, ends in 5.

To "remove" 3 0's, and turn last digit to 5, Xx = 8*k
(Cc00 ± 100) / Xx will *not* be half-way case, since 100/8 = 12.5, not an integer.

We can use gap test to check for half-way case. This work on a 2-digits calculator:

→ if (Cc00+100)/Xx - Qq < Qq - (Cc00-100)/Xx then Qq := Qq - 1;

Example, if Cc00 = 900, Xx = 40, Qq = round(22.5) = 22

LHS = high gap = 25 - 22 = 3
RHS = low gap  = 22 - 20 = 2

This meant Qq were already rounded-down, and no correction needed for it.
Find all posts by this user
Quote this message in a reply
12-17-2020, 07:50 PM
Post: #37
RE: (42, all flavours) Integer Division - how?
(12-17-2020 06:41 PM)Albert Chan Wrote:  Your half-way-case had a typo. It should be b := Qq % c

Thanks, I corrected it.

Quote:If Cc00/Xx fractional part is 0.5, it implied Cc000 / Xx is integer, ends in 5.

No.. each 0 is the length of a half integer, so for a 12 digit machine it is 000000 and for Free42/DM42 it's 17 zeroes ;-)

Cc00 = Qq*Xx + Rr is the first part of dividing a multiprecision number stored in sequential registers by a number not exceeding a single register:

R1 R2 R3 R4 R5
Aa Bb Cc Dd Ee ... / Xx

At each step you have the carry from the previous step, Cc, and so you have to do
CcYY = Qq*Xx + Rr

with Rr next step's Cc.

We can only work with 2 letters at the same time, so first

Cc00 = Qq*Xx + Rr (as previously covered)
t := Yy-Xx+Rr; (because Yy+Rr may overflow)
Cc := MOD(t,Xx);
IF t>0 then
Qq := Qq + (t-Cc)/Xx + 1;
end;
Yy := Qq;

Werner

41CV†,42S,48GX,49G,DM42,DM41X,17BII,15CE,DM15L,12C,16CE
Find all posts by this user
Quote this message in a reply
12-17-2020, 10:03 PM (This post was last modified: 12-17-2020 10:29 PM by Albert Chan.)
Post: #38
RE: (42, all flavours) Integer Division - how?
(12-17-2020 06:41 PM)Albert Chan Wrote:  If Cc00/Xx fractional part is 0.5, it implied Cc000 / Xx is integer, ends in 5.

To "remove" 3 0's, and turn last digit to 5, Xx = 8*k
(Cc00 ± 100) / Xx will *not* be half-way case, since 100/8 = 12.5, not an integer.

We can use gap test to check for half-way case. This work on a 2-digits calculator:

→ if (Cc00+100)/Xx - Qq < Qq - (Cc00-100)/Xx then Qq := Qq - 1;

(12-17-2020 07:50 PM)Werner Wrote:  No.. each 0 is the length of a half integer, so for a 12 digit machine it is 000000 and for Free42/DM42 it's 17 zeroes ;-)

I misunderstood. "0" = h zeroes, not 1 zero (*)
But, the logic holds. Just replace "hidden" 1 with h

\(\large{CcOO \over Xx}\) is half-way cases → \(Xx = 2^{2h+1} \cdot k\)

\(\large{CcOO\;±\;IOO \over Xx}\) will *not* be half-way case, since \(
\large{10^{2h} \over 2^{2h+1}} = {5^{2h} \over 2}\), not an integer.

Gap test work with with "half-integers" too.

(*) I had assumed "0" = 1 zero, on a 2-digit calculator, and checked how the code perform.
Code:
Cases      %   Types
  404     8.2  FRAC(Qq) > 0, return Qq
 2420    48.9  Mod test, return Qq
 2050    41.4  Mod test, return Qq - 1
   76     1.5  Half-way cases (evenly split 38/38)
-------------
 4950     100
Find all posts by this user
Quote this message in a reply
12-18-2020, 12:08 AM (This post was last modified: 12-19-2020 12:56 AM by Albert Chan.)
Post: #39
RE: (42, all flavours) Integer Division - how?
remainder() based idiv is very elegant.
PHP Code:
function idiv6(ab)    -- assumed b 0
    local r 
a/b
    local q 
floor(r)
    if 
== and remainder(ab) < 0 then q=q-1 end
    
return q
end 

Above is similar to this (6a version possibly faster, only tried to get remainder sign)
PHP Code:
function idiv6a(ab)   -- assumed b 0
    local r 
a/b
    local q 
floor(r)
    if 
~= r then return q end
    r 
% (b+b)
    
local R and or r-b
    local h 
0.5*b    -- halfway
    
if or == h then return q end
    
return 1
end 

All the code for mod/half-way test is the last if line.

R < h:

if residual is less than half of b, q had rounded-down. For floor divide, no need for correction.

r == h:

if residual is half of b, q (because of round-to-even) is even.
But, r also guaranteed even q -> no need for correction.

Update: slightly faster version 6b, removed R, and r shifted, with range = [-2h, 2h)

PHP Code:
function idiv6b(ab)   -- assumed b 0
    local r 
a/b
    local q 
floor(r)
    if 
~= r then return q end
    r 
% (b+b) - b
    local h 
0.5*b     -- halfway
    
if <= -or (and >= 0then return q end
    
return 1
end 
Find all posts by this user
Quote this message in a reply
12-19-2020, 03:14 AM (This post was last modified: 12-20-2020 11:09 AM by Albert Chan.)
Post: #40
RE: (42, all flavours) Integer Division - how?
The chance of hitting half-way cases are small.
We can do the remainder check by then. This made the code simpler too.

PHP Code:
function idiv6c(ab)   -- assumed b 0
    local r 
a/b
    local q 
floor(r)
    if 
~= r then return q end
    r 
= (a%b) * 2
    
if == b then r % (b+bend
    
return and q-or q
end 

Example, for 2-digit calculator, above/below halfway decides how to correct q

a/b = 700/22 = 31.81..., rounded to q = 32
ulp = (a%b)/b = 18/22 ≈ 0.81 ULP, above halfway, return q-1 = 31

a/b = 700/23 = 30.43..., rounded to q = 30
ulp = (a%b)/b = 10/23 ≈ 0.43 ULP, below halfway, return q = 30

---

For halfway case, r is updated, r = a % (b+b)
Previously, r = b, this implied updated r = b/2, or 3b/2
To maintain even q, and remainder range of ±b, 3b/2 got shifted down 2b, to -b/2

Only -b/2 remainder require correction: a/b = q - 1/2, rounded-up to q, floor-divide should be q-1

Example, for 2-digit calculator

a/b = 460/40 = 11.5, rounded to q = 12 (half-way to even)

r = (a%b)*2 = 40 = b --> signal halfway case
r = a%(b+b) = 60 --> signal remainder = -20, floor-divide should return q-1 = 11

Reuse the code for non-halfway: return r > b and q-1 or q

For above example, r=60, b=40, thus it return q-1 = 11, as expected

Update: removed variable h=b/2. This made the code more robust.

Previously, we had r = a%b, followed by r = a%(b+b) if halfway.
Last line was return r > h and q-1 or q

For halfway case, we may hit with *slight* floor-mod rounding.

If a is negative, a % (b+b) = (b+b) - |a| % (b+b) = 4h - 3h = h
But, 3h term might not be exact, thus last h may have *slight* rounding error.

Update 2: fix a typo for halfway test, r == 0 should be r == b
Find all posts by this user
Quote this message in a reply
Post Reply 




User(s) browsing this thread: 1 Guest(s)