TGB1718 Posted June 22 Share Posted June 22 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. Quote Link to comment Share on other sites More sharing options...
Rybags Posted June 22 Share Posted June 22 How are you writing out the values? Your list of comparison errors there is showing the same values for array element and number read in. Maybe a program error somewhere? Quote Link to comment Share on other sites More sharing options...
TGB1718 Posted June 22 Author Share Posted June 22 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. Quote Link to comment Share on other sites More sharing options...
Rybags Posted June 22 Share Posted June 22 (edited) 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 June 22 by Rybags 2 Quote Link to comment Share on other sites More sharing options...
TGB1718 Posted June 22 Author Share Posted June 22 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 2 Quote Link to comment Share on other sites More sharing options...
+yetanothertroll Posted June 22 Share Posted June 22 (edited) 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 June 22 by yetanothertroll IEEE 754 will make anyone go "IEEE!!!" Quote Link to comment Share on other sites More sharing options...
Rybags Posted June 22 Share Posted June 22 (edited) 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 June 22 by Rybags 2 Quote Link to comment Share on other sites More sharing options...
TGB1718 Posted June 22 Author Share Posted June 22 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 2 Quote Link to comment Share on other sites More sharing options...
TGB1718 Posted June 22 Author Share Posted June 22 @Rybags you posted your response, just as I clicked, does confirm about the last digit 1 Quote Link to comment Share on other sites More sharing options...
dmsc Posted June 22 Share Posted June 22 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! 2 Quote Link to comment Share on other sites More sharing options...
Rybags Posted June 22 Share Posted June 22 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. 1 Quote Link to comment Share on other sites More sharing options...
phaeron Posted June 22 Share Posted June 22 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. 1 1 Quote Link to comment Share on other sites More sharing options...
thorfdbg Posted June 22 Share Posted June 22 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. 2 Quote Link to comment Share on other sites More sharing options...
thorfdbg Posted June 22 Share Posted June 22 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. Quote Link to comment Share on other sites More sharing options...
+yetanothertroll Posted June 22 Share Posted June 22 To be fair, Atari BCD floating point is almost embarrassingly round-trippable through text and back, if only the Print, Str, and Val routines didn't mangle the tenth digit that only shows up sometimes 1 Quote Link to comment Share on other sites More sharing options...
+yetanothertroll Posted June 22 Share Posted June 22 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 2 Quote Link to comment Share on other sites More sharing options...
phaeron Posted June 22 Share Posted June 22 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. 2 Quote Link to comment Share on other sites More sharing options...
Aesthetic Debris Posted June 22 Share Posted June 22 (edited) 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 June 22 by Aesthetic Debris 1 Quote Link to comment Share on other sites More sharing options...
SoundGammon Posted June 22 Share Posted June 22 Are you using Atari Basic "B" or "C"? Just a thought. "B" had a bug in it. 1 Quote Link to comment Share on other sites More sharing options...
dmsc Posted June 23 Share Posted June 23 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! 1 Quote Link to comment Share on other sites More sharing options...
thorfdbg Posted June 23 Share Posted June 23 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. Quote Link to comment Share on other sites More sharing options...
thorfdbg Posted June 23 Share Posted June 23 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. 1 Quote Link to comment Share on other sites More sharing options...
thorfdbg Posted June 23 Share Posted June 23 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. Quote Link to comment Share on other sites More sharing options...
ClausB Posted June 23 Share Posted June 23 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. 2 Quote Link to comment Share on other sites More sharing options...
TGB1718 Posted June 23 Author Share Posted June 23 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 2 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.