mappo
mappo

Reputation: 476

How do I work around ⎕CT limitations?

Here's my basic problem:

LOW←6     ⍝ lower bound
UPP←225   ⍝ upper bound
INC←0.01  ⍝ increment
VAL←50    ⍝ value

I want to determine if VAL is a multiple of the increments. My initial solution was to check if (VAL-LOW)÷INC is an integer. This is where I ran into ⎕CT issues (the following is not my actual code, but it illustrates my case).

V←(VAL-LOW)÷INC
W←⌊0.5+|V
V
    4400
W
    4400
V=W
    0
|V-W
    1.somethingE¯13

Oh dear! (Note: of course this doesn't happen when I run the code "barefoot" - only when it's deep in a call stack in an actual environment.)

The second idea was not very elegant, but it seemed sound on paper:

V←0 12⍕(VAL-LOW)÷INC
0=⍎(1+V⍳'.')↓V

Format V to 12 places, drop the decimal point and everything to the left of it. Execute the rest and check if it equals zero. That worked ok, until it didn't and I was left with twelve nines.

D'oh!

Then it struck me that maybe I don't always need 12 decimals. In fact I only need as many as there are in INC:

DEC←(⌽⍕INC)⍳'.'
V←0 DEC⍕(VAL-LOW)÷INC
0=⍎(1+V⍳'.')↓V

I don't know ... it's getting messier and messier. What happens when UPP is huge and INC is 1000? Is there a more clever way of doing this?

The naive solution was to, based on LOW, INC and UPP, generate a list of valid VALs, but such a solution is always at risk of running out of working memory.

Upvotes: 2

Views: 120

Answers (2)

Tobia
Tobia

Reputation: 18811

This is an issue in all programming languages, which only becomes a problem if you don't understand it.

Decimal values in most (all?) APL implementations are stored by the computer as binary floating point values. Binary means that both the integer and the decimal part are made by summing respectively positive and negative powers of two:

3 = 2 + 1 = 21 + 20 = 11 in binary

3.5 = 2 + 1 + 1/2 = 21 + 20 + 2-1 = 11.1 in binary

3.125 = 2 + 1 + 1/8 = 21 + 20 + 2-3 = 11.001 in binary

0.75 = 1/2 + 1/4 = 2-1 + 2-2 = 0.11 in binary

Floating point means that only the top N most significant binary digits are stored, along with an exponent value that "shifts" them to the left or to the right.

This only becomes a problem when you try to use this internal format (binary floating point) to store exact decimal values, because in most cases it can't.

That's because many (rational) numbers that have a finite decimal expansion in base 10, do not in base 2:

0.1 = 2-4 + 2-5 + 2-8 + 2-9 + 2-12 + 2-13 + ... = 0.0 0011 0011 0011... (periodic)

If your APL implementation supports a "decimal" variable type, you may use that. Otherwise, the usual workaround (and the way "decimal" variables are usually implemented in languages or libraries that supply them) is to work with a couple of integers, a value and an exponent in base 10.

For example: 0.00123 = 123 × 10-5, so you may represent it as the vector of integers: 123, -5

Now the fun part is implementing all the arithmetic operations you need on such vectors, in your flavor of APL, making sure to only use integer arithmetic under the hood at all times. That is left as an exercise to the reader :-)

Of course, if you never expect to need more than X decimal digits, you may use regular integers and regular integer arithmetic, with the convention that your variables represent cents, or thousandths, or millionths, or some other fraction. That's how money values are (should be) handled in computer programs, by the way, to avoid any rounding errors: as integer values expressed in cents.

Upvotes: 0

Lobachevsky
Lobachevsky

Reputation: 1252

Set []PP to 16 or 17, whatever the system maximum is, so that what you see is what you get. The system may conveniently round displayed values to make them appear to be integer valued when in reality they are not. Note this affects only what you see, the result of ⍕, and not any computations themselves. Accuracy vs. Precision.

Then try setting []CT to something relatively "large", maybe 1e-10 or 1e-8. This will make the relational function appear to behave less finicky. See what that does.

You might also try changing the comparison to be something like

0.000001 > | V - W

i.e. do the []CT thing yourself. Be sure to set []CT to zero.

All should be well if your numbers are within the range of ordinary integers or integers representable in 64 bit floating point, around 2e50.

Upvotes: 2

Related Questions