General APL language issues

Dyalog 12.1 Classic

I cannot understand the following result:
Code: Select all
`           ⎕CT←0       (⊃10*-⍳22)≡⊃10*-⍳900`

Can we always trust in []CT ?

giangiquario

Posts: 46
Joined: Thu Nov 26, 2009 8:55 am
Location: Milano, Italia

Power to an integer right argument remembers its arguments and result. If on the next call the value of the right argument differs from the previous call by 1, 0, ¯1 the result is computed by multiplication/division from the previous result.

What is seen here is the difference between calculating the result by 90 multiplications compared with calculating the result by 22 divisions.

So the question is not really about comparison tolerance but about how much an APL can try to optimise its performance. The default value of ⎕CT is really there to mask such differences.
Geoff|Dyalog

Posts: 43
Joined: Wed May 13, 2009 12:36 pm

Sorry, that should have said "22 multiplications"
Geoff|Dyalog

Posts: 43
Joined: Wed May 13, 2009 12:36 pm

Incidentally, with version 13 I can do:

Dyalog APL/S-64 Version 13.0.7951
Unicode Edition
DEBUG Build
Pre-release Development Build
Tue Feb 8 09:36:28 2011
clear ws
⎕fr←1287
⎕pp←34
⎕dct←0
(⊃10*-⍳22)≡⊃10*-⍳90
1
⊃10*-⍳90
0.1

This is actually a demonstration of one of the advantages of decimal floating point over binary floating point. If the left argument to power had been a power of 2 rather than a power of 10 then binary would perform more in accordance with pure mathematics. However, a power of 10 is more common in code than a power of 2 and floating point hardware/software is only an approximation to pure mathematics.
Geoff|Dyalog

Posts: 43
Joined: Wed May 13, 2009 12:36 pm

Just to make sure that we are really talking about ⎕CT here:
What is the value of ⎕ML in your examples, guys?

You know that:
⎕ml←0
(⊃10*-⍳22)
0.1
⎕ml←1
(⊃10*-⍳22)
0.1
⎕ml←2
(⊃10*-⍳22)
0.1 0.01 0.001 0.0001 0.00001 0.000001 1E¯7 1E¯8 1E¯9 1E¯10 1E¯11 1E¯12 1E¯13 1E¯14 1E¯15 1E¯16 1E¯17
1E¯18 1E¯19 1E¯20 1E¯21 1E¯22
⎕ml←3
(⊃10*-⍳22)
0.1 0.01 0.001 0.0001 0.00001 0.000001 1E¯7 1E¯8 1E¯9 1E¯10 1E¯11 1E¯12 1E¯13 1E¯14 1E¯15 1E¯16 1E¯17
1E¯18 1E¯19 1E¯20 1E¯21 1E¯22

In cases 0 and 1 we are compariing he first values of two different series of numbers.
(⊃10*-⍳22)≡⊃10*-⍳90 must match.
In cases 2 and 3 we are comparing two different series as a whole.
(⊃10*-⍳22)≡⊃10*-⍳90 must not match, no matter what ⎕CT, because of the different numbers of elements.
uwejanza

Posts: 19
Joined: Tue Mar 09, 2010 2:01 pm
Location: Nürnberg, Germany

The []ML is 0

giangiquario

Posts: 46
Joined: Thu Nov 26, 2009 8:55 am
Location: Milano, Italia

First, let's look at some experimental results in Dyalog 12.1:

Code: Select all
`      ⎕pp←17      ⎕ml←0      ⎕ct←0      ⊃10*-⍳100.1      ⊃10*-⍳110.09999999999999998      ⊃10*-⍳510.10000000000000006      ⊃10*-⍳11510.09999999999999992`

If I got Goeff's post right, the interpreter should in any case first calculate the value of "10 to the power of the first element of the vector" of negative integers, which in any of the cases would be 10 to the power of -1. If afterwards the interpreter chooses to use this value as a base to multiply with or divide by some other value, this multiplication or division operation should not harm the base value calculated in the first place. But as we can see in the example above, the value is harmed.
There seem to be several algorithms involved in calculating the series of powers, depending of the length or value of the progression vector used as the right argument of the * function.

Geoff pointed out that decimal floating point numbers would cure such cases. I tend to agree for cases where the left argument of the * function is a product of 2s and 5s. I tend to doubt for all other integers.

Code: Select all
`      ⊃9*-⍳70.1111111111111111      ⊃9*-⍳110.11111111111111109      ⊃9*-⍳510.11111111111111105      ⊃9*-⍳11510.11111111111111097`

Even with decimal floating point numbers implemented we may still need a little bit of tolerance in comparisons, with ⎕CT set to a value different to zero.

Uwe
uwejanza

Posts: 19
Joined: Tue Mar 09, 2010 2:01 pm
Location: Nürnberg, Germany

I had to track this through a debugger to understand this. Dyalog APL's arithmetic functions change algorithm at 8 elements in the array. The first algorithm is to do the obvious. The second uses a vector approach (higher setup cost, lower per element cost). The first algorithm walks the array forward the second algorithm walks the array backwards. No good reason for the difference just the way the coder thought.

Now the scalar function (called by both methods) remembers its arguments and result. If, on the next call the left argument is the same and the right argument differs by +1, then the new result is derived from the previous result.

So in the
`      ⊃10*-⍳11`

case, the value is derived from successive multiplications following the initial derivation of
`      10*¯11`

similarly for
`      ⊃10*-⍳51`

the value is derived from successive multiplications following the initial derivation of
`      10*¯51`

Note the +1 difference is for true for v12.1. Current code for v13.0 also does ¯1 and 0 this way.
Geoff|Dyalog

Posts: 43
Joined: Wed May 13, 2009 12:36 pm