Jump to content
IGNORED

Floating Point Atari BASIC


Recommended Posts

I'm writing a program that will be running on a vanilla 800XL that's extensively using floating point numbers, the

data will be saved to disk. On subsequent uses, READ back from disk, however during testing where I store

a large number of values in an array, save the array to disk, then read the data back and compare with what's

still in memory.

 

What I'm finding is that although the data looks identical a simple comparison fails, is this a known issue with

the floating point package ?

 

Here's a portion of the output:- literally doing "IF A(I,J)<>NUM THEN ..." where A(I,J) is the array element and NUM is the value read in from disk.

 

VALUE ERROR 92.0263061,92.0263061
VALUE ERROR 92.2139892,92.2139892
VALUE ERROR 42.2384033,42.2384033
VALUE ERROR 34.2687377,34.2687377
NUMBER OF ERRORS=600

 

There are 600 values, so they all fail comparison :(

 

As I only need accuracy to 2 decimal places, I found that if I do the following before the save and put in the array, it works fine, but

for 600 entries, it slows things down a bit and quite untidy.

 

TMP=INT(A(I,J))*100:A(I,J)=TMP/100

 

Would have preferred to use the full original number and only do the 2 decimal place conversion before displaying the value

as they don't all get displayed, only when a calculation is done.

 

Link to comment
Share on other sites

41 minutes ago, Rybags said:

How are you writing out the values?

Straightforward 

Channel 1 is open for output

 

85 PRINT #1,A(I,J)

and this to read:-

140 INPUT #1,NUM

 

43 minutes ago, Rybags said:

Your list of comparison errors there is showing the same values for array element and number read in.

Yes, I can't see any difference, however BASIC seems to think they are different

 

43 minutes ago, Rybags said:

Maybe a program error somewhere?

 

I can't see any, it simply generates 600 random numbers in a loop and pops the values into and array :-

40 A(I,J)=RND(I)*100+1

 

Then in another loop print's the values to disk.

 

Then another loop to read them back and compare, it's just a simple test program, no funny processing.

Link to comment
Share on other sites

I've been able to replicate the error situation.

I think it's possibly a Basic and/or FP bug.

Funnily enough, if you run this program with line 30 assigining A(X) = 1+RND(0) you get no errors.

 

 

10 OPEN #1,8,0,"H:NUMBERS"
20 DIM A(500)
30 FOR X=1 TO 500:A(X)=RND(0):NEXT X
100 FOR X=1 TO 500:? #1;A(X)
190 NEXT X
200 CLOSE #1
300 OPEN #1,4,0,"H:NUMBERS"
310 FOR X=1 TO 500:INPUT #1,NUM
320 IF NUM<>A(X) THEN ? X,NUM;" ";A(X):E
RR=ERR+1
390 NEXT X
400 CLOSE #1
410 ? "ERROR COUNT";ERR

 

 

My theory - we have 10 digits of precision but I think Basic will suppress the final fractional digit.

So, when we write to a file that digit is lost but when we do a comparison it is wrong.

 

I agree with your earlier comment - best to do some rounding of the values first, or just put up with the fact you might lose that last digit after writing to a file.

 

Edited by Rybags
  • Like 2
Link to comment
Share on other sites

1 hour ago, Rybags said:

I think it's possibly a Basic and/or FP bug.

Think it's an FP bug, does the same in Altirra BASIC.

1 hour ago, Rybags said:

My theory - we have 10 digits of precision but I think Basic will suppress the final fractional digit.

I think you're right, I found any number < 10.00000 is fine, anything > 10.00000 fails

  • Like 2
Link to comment
Share on other sites

Is saving the data in binary rather than text an option? Or maybe roll your own text format that's closer to what's going on in the internal  BCD format, like this?

 

Understanding internal BCD representation

 

Edit: It seems "equal" <> equal in most floating point representations

 

https://codingnest.com/the-little-things-comparing-floating-point-numbers/

Edited by yetanothertroll
IEEE 754 will make anyone go "IEEE!!!"
Link to comment
Share on other sites

The problem is we lose the last FP digit when writing it out.

 

You can actually reveal it - try something like this:

 

A=RND(0)

? A

 

Once it prints out, insert ? A- before the number printed out.  The result will be something like 7E-10 - the exponent value should = the lost digit.

 

example:

 

A=RND(0)

READY
?A
0.664947509

READY
?A-0.664947509
7E-10

READY

Edited by Rybags
  • Like 2
Link to comment
Share on other sites

6 minutes ago, yetanothertroll said:

Is saving the data in binary rather than text an option? Or maybe roll your own text format that's closer to what's going on in the internal  BCD format, like this?

I suppose you could save the data that way, but you would need to access each value in the variable table and PUT each byte of the number to disk,

then you have the problem of reading the values back using the same method, completely d0-able, but very complex. 

 

1 hour ago, Rybags said:

My theory - we have 10 digits of precision but I think Basic will suppress the final fractional digit.

So, when we write to a file that digit is lost but when we do a comparison it is wrong

I have a play with a couple of variables and the Variable Value Table, this is what I found:-

A is the initial value before writing to disk , display as (83.8811645)

B is the value after being read back from disk, display as (83.8811645)

 

INTERNAL VALUE FOR A
0 0 64 131 136 17 100 85

 

INTERNAL VALUE FOR B
0 1 64 131 136 17 100 80

 

So it does confirm, when the value is being read back from disk and converted back to a floating point number it introduces a minor error.

 

On disk this is the value saved 38 33 2e 38 38 31 31 36 34 35 = 83.8811645

 

So it looks like the best way for me is to convert to an INT * 100, then divide by 100

  • Like 2
Link to comment
Share on other sites

Hi!

 

This is not a bug, but the way floating-point (in the Atari) works.

 

TurboBASIC included "PUT%" and "GET%" statements that allow writing and reading the binary data of the FP value, this is faster and will preserve the complete internal value; or you can use BPUT and BGET over the entire array memory to quickly save and load the whole array.

 

Comparing floating-point values for equality, on any language or computer, is prone to errors and can give misleading results.

 

Have Fun!

  • Like 2
Link to comment
Share on other sites

Can the differences be tolerated?

If not you can just do rounding.  Like A=A*10 followed by A=A/10.

Fairly sure that should strip out the hidden digit, and could be condensed into one statement rather than 2.

  • Like 1
Link to comment
Share on other sites

The issue is in the AFP routine that converts ATASCII numbers to floating point. It shifts in digits into the mantissa until the 9th digit is non-zero, then only counts digits, then does one additional left digit shift if needed at the end. This results in unnecessarily dropping one digit of precision at times. However, to preserve that digit with the algorithm used, it would have needed to sometimes do a right digit shift at the end, and the math pack doesn't seem to have that routine. FASC, on the other hand, can print all 10 mantissa digits in cases that AFP can't fully parse.

 

I'd tend to call this a bug in AFP, as the documentation implies that the precision generally should be kept and it would be possible to do so. The reason you'd generally drop the 9th digit is to try to maintain consistent precision on screen, so that numbers didn't alternate between 9 or 10 digits of precision. The docs don't indicate that the math pack does this, but there does seem to been some attempt as BASIC does have code to truncate off the 10th digit in PRINT (though arithmetic operations like add don't).

 

As a side note, this is actually an issue in modern systems too, as some C runtime libraries have bugs where FP numbers don't round-trip through conversion to text even if more than enough significant digits are requested. Recent CRTs have been fixed, but the algorithms to do this for binary floating point are much more involved than for BCD floating point.

 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

1 hour ago, phaeron said:

The issue is in the AFP routine that converts ATASCII numbers to floating point. It shifts in digits into the mantissa until the 9th digit is non-zero, then only counts digits, then does one additional left digit shift if needed at the end. This results in unnecessarily dropping one digit of precision at times.

Well, it is one out of many lousy implementation choices the floating point package picked, and this particular one was addressed in Os++ and its improved math pack. Thus, potentially, in this particular case, the issue may go away with a better mathpack.

 

There is, however, a big "however". Namely: Converting floating point to any other representation requires rounding, and rounding will in general create loss. From this follows that you should please not store floating point number as ASCII, but in their internal representation (6 bytes each), and you should neither compare floating point numbers for equality, as this operation is pretty meaningless for numbers that are only represented up to a limited precision.

 

 

  • Like 2
Link to comment
Share on other sites

6 hours ago, dmsc said:

This is not a bug, but the way floating-point (in the Atari) works.

Well.... trivially, it works of course how it works, but there are two questions: a) could it work better given the constraints implied by the representation for numbers that have been picked, and b) is that really a good way how it works.

 

a) Even with the lousy base-100 representation the floating point package uses, there is sufficient implementation freedom to implement this in a better way avoiding at least some of the pitfalls

b) No. A base-100 representation is necessarily a lot less precise than a base-2 representation and much more susceptible to rounding errors.

 

Link to comment
Share on other sites

2 minutes ago, thorfdbg said:

Well.... trivially, it works of course how it works, but there are two questions: a) could it work better given the constraints implied by the representation for numbers that have been picked, and b) is that really a good way how it works.

 

a) Even with the lousy base-100 representation the floating point package uses, there is sufficient implementation freedom to implement this in a better way avoiding at least some of the pitfalls

b) No. A base-100 representation is necessarily a lot less precise than a base-2 representation and much more susceptible to rounding errors.

 

Maybe, but at least 0.2 + 0.3 will equal 0.5 in Atari. It just has different gotchas than the IEEE 754 binary format we've all grown to love and loathe since the 8087

  • Like 2
Link to comment
Share on other sites

17 minutes ago, thorfdbg said:

There is, however, a big "however". Namely: Converting floating point to any other representation requires rounding, and rounding will in general create loss.

This isn't a given. Binary floating point conversion to text and back is only lossy if the conversion routine doesn't preserve full precision or insufficient digits are used. Using enough decimal digits to cover the full mantissa precision and conversion routines that convert to nearest is enough to cleanly round-trip numbers. And for BCD floating point, conversion to text requires no rounding.

 

17 minutes ago, thorfdbg said:

you should neither compare floating point numbers for equality, as this operation is pretty meaningless for numbers that are only represented up to a limited precision.

No, floating point equality isn't meaningless. I don't know why people make this absurd leap from roundoff being inherent in floating point arithmetic to equality comparisons being impossible. If this were the case you couldn't safely do IF X=1 in a BASIC program.

 

  • Like 2
Link to comment
Share on other sites

24 minutes ago, phaeron said:

No, floating point equality isn't meaningless. I don't know why people make this absurd leap from roundoff being inherent in floating point arithmetic to equality comparisons being impossible. If this were the case you couldn't safely do IF X=1 in a BASIC program.

 

In simple cases it works, but after repeated calculations that involve fractional parts it's very easy for tiny fractional errors to creep in. Once that happens, exact tests for equality fail when you wouldn't expect it. Its much better to check if values are within a suitably small delta of each other, and consider them equivalent if they fall in that range.

Edited by Aesthetic Debris
  • Like 1
Link to comment
Share on other sites

Hi!

4 hours ago, phaeron said:

This isn't a given. Binary floating point conversion to text and back is only lossy if the conversion routine doesn't preserve full precision or insufficient digits are used. Using enough decimal digits to cover the full mantissa precision and conversion routines that convert to nearest is enough to cleanly round-trip numbers. And for BCD floating point, conversion to text requires no rounding.

I suspected that the original Atari code reduced precision to avoid printing the usually bad last digit, but then again, it does not round the value, so it will not fix simple errors, so I probably was wrong, and it is simply a coding limitation or bug.

 

4 hours ago, phaeron said:

No, floating point equality isn't meaningless. I don't know why people make this absurd leap from roundoff being inherent in floating point arithmetic to equality comparisons being impossible. If this were the case you couldn't safely do IF X=1 in a BASIC program.

I agree, as the Goldberg paper (What Every Computer Scientist Should Know About Floating-Point) says in, section 3:

Incidentally, some people think that the solution to such anomalies is never to compare floating-point numbers for equality, but instead to consider them equal if they are within some error bound E. This is hardly a cure-all because it raises as many questions as it answers.

 

And replying on another myth, with correctly rounded binary floating-point arithmetic, 0.2 + 0.3 is exactly 0.5. It is 0.2 + 0.1 that is not equal to 0.3, but 0.2 + 0.1 + 0.1 is exactly 0.4. The problematic value is 0.7 + 0.1, that gives less than 0.8.

 

IMHO, thanks to JavaScript rules, newer libraries are better at correctly printing binary-floating point numbers, as the ECMA specification requires that x == ToNumber(ToString(x)).

 

Have Fun!

 

  • Thanks 1
Link to comment
Share on other sites

9 hours ago, yetanothertroll said:

Maybe, but at least 0.2 + 0.3 will equal 0.5 in Atari. It just has different gotchas than the IEEE 754 binary format we've all grown to love and loathe since the 8087

"different" is an euphemism. "Many more" is more appropriate. It has serious rounding issues.

Link to comment
Share on other sites

7 hours ago, SoundGammon said:

Are you using Atari Basic "B" or "C"? Just a thought. "B" had a bug in it.

Yes, but that bug is not related to the math package. Actually, A had a similar bug, just in another place (all in the memory mover). That said, actually, all revisions have bugs, but the  Rev.B bug is quite serious as it can lock up the Basic interpreter.

  • Like 1
Link to comment
Share on other sites

8 hours ago, phaeron said:

No, floating point equality isn't meaningless. I don't know why people make this absurd leap from roundoff being inherent in floating point arithmetic to equality comparisons being impossible. If this were the case you couldn't safely do IF X=1 in a BASIC program.

 

They are not impossible, but they can (and do, as seen here) give you false results if used naively. What is the right thing to do depends of course on the problem you want to solve. In this particular case, the right thing to do would be to use better math functions (Os++ would probably work here), or store the numbers in their original format, avoiding the conversion.

Link to comment
Share on other sites

When I worked at New Dimensions in Computing in 1981 they asked me to find out why their accounting program was dropping pennies. It was in MS BASIC for CP/M on a Vector Graphic S100 system. The floating point variables were in dollars, so 0.01 dollars was not exact because of the binary representation. I added some rounding to fix it. Also sales tax percentage, 4% or 0.04, was not exact plus there were rounding rules specified in the law.

 

Atari's BCD representation would not have lost pennies, nor would have MS if the variables had been defined in cents instead of dollars.

  • Like 2
Link to comment
Share on other sites

1 hour ago, ClausB said:

When I worked at New Dimensions in Computing in 1981 they asked me to find out why their accounting program was dropping pennies.

Similar experience, I worked for a major UK bank, when I was asked to add some new functionality I noticed customer interest payments

were always rounded up in the banks favour :)

 

  • Haha 2
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...