BASIC INT rounding issue
Jon Abbott (1421) 2651 posts |
BASIC exhibits this issue, BASIC64 does not. Test up to BASIC 1.85 and back to BASIC 1.04 in RO2, so it’s not a new issue. EDIT: Tested directly after issuing *BASIC – so using the default @% Repros PRINT VAL("0.01")*100 1 PRINT INT(VAL("0.01")*100) 0 PRINT VAL("0.02")*100 2 PRINT INT(VAL("0.02")*100) 1 |
Clive Semmens (2335) 3276 posts |
It’s not an issue at all. It’s an entirely acceptable consequence of the rounding to different precisions. I’d be interested to see the results for BASIC64, actually – it’s surely not 1,1 and 2,2 is it? That’s definitely dodgy – it implies rounding up, by an admittedly very small amount. INT is defined to round down – not sure what PRINT is supposed to do with the least significant digit of real numbers, nor for that matter what VAL is supposed to do with the least significant binary digit of a decimal conversion. If they’re supposed to round down too, then both should have strings of 9s, of course, but I suspect they’re supposed to round to nearest.* What’s the current value of @% in each case?
|
Rick Murray (539) 13850 posts |
Nah, this line is dodgy:
Let’s do it step by step: >PRINT VAL("0.01") 0.01 >PRINT VAL("0.01")*100 1 >PRINT INT(VAL("0.01")*100) 0 That doesn’t make sense. 100 * 0.01 is exactly 1, so why is INT getting it wrong? You know, I think there’s a bug lurking in here… >PRINT INT( 1000 * 0.001 ) 1 >PRINT INT( 100 * 0.01 ) 0 >PRINT INT( 10 * 0.1 ) 1 Tested using regular BASIC, 1.85 3rd October 2022 in ROM 5.29, 16th December 2023.
That’s why we always add 0.5 when we want “round to nearest” behaviour.
As a side note, what’s the incantation to stop BASIC reducing long numbers to exponent form? It’s annoying to see the result as 1.2345E12 because I then have to do extra mental work to make that a real number. |
David Pitt (9872) 363 posts |
>PRINT~@% 90A >PRINT 100*0.01 1 >@%=&a0b >PRINT 100*0.01 0.9999999998 |
Clive Semmens (2335) 3276 posts |
Thanks, David – I’m at the Mac, not the Pi, or I’d have done it myself. That’s exactly the sort of thing I’d expect though! :-) |
Clive Semmens (2335) 3276 posts |
Because it does 100 * 0.01 in two steps: the first thing it has to do is convert 0.01 to binary before it can do the multiplication, and that involves a rounding. THEN it does the multiplication. The result is a tiny tad less than 1, and INT then rounds it down. INT always rounds down; floating point operations probably round the last digit to nearest, but I don’t know. If they do round to nearest, then whether 100 * 0.01 is a tad less than one or a tad more could well depend on the precision being used. |
Jon Abbott (1421) 2651 posts |
It’s not the fact its wrong that’s really the issue, its the inconsistent result. I don’t think its unreasonable to assume the same rounding method would be used whenever FP rounding is performed. |
Graeme (8815) 106 posts |
That is the problem. It is not ‘exact’. At least not in floating point numbers. The value 0.01 is a recurring number in binary. It needs to have that recurring end either rounded off or chopped off making the value very close but not quite accurate. Try 1 divided by 100 in VFP and then check the inexact flag in the FPSCR. You will see it has been set. The value in hex from VFP: 3F8 47AE1 47AE1 47B…. You can see VFP rounding up that last A to a B to get as close as possible to 0.01. You may find FPE or BASIC does not round up that last hex digit which can make your results vary a little. When multiplying an inexact value, you are multiplying the inexactness. To really complicate it further, BASIC will be rounding results in the PRINT command. |
Clive Semmens (2335) 3276 posts |
It seems likely that it is the same rounding method: round to nearest, which would sometimes be up and sometimes down, depending on what the last binary digits happen to be, which would vary according to the precision being worked to. |
Rick Murray (539) 13850 posts |
…this is why I hate maths. |
Clive Semmens (2335) 3276 posts |
8~( |
Paul Sprangers (346) 525 posts |
Exactly that. 0.01 is unambiguous in decimal, but it is a repetitive fracture in binary – and therefore rounded. (Fractions in numeral systems other than 10 are very unintuitive – certainly for me!) |
Clive Semmens (2335) 3276 posts |
Think of it like this, Rick: What’s 3 * 1/3? Exactly 1, of course. But now try to do it in floating point: it’s 3 * (approximately) 0.3333333333333 = 0.9999999999999.
But 3 * 2/3 = 3 * 0.666666666667 = 2.000000000001.
|
Clive Semmens (2335) 3276 posts |
I bet fractions in decimal are very unintuitive for folks from Betelgeuse (who think in hexadecimal…) – or for that matter for ancient Babylonians (sexagesimal – base 60)! I was very lucky to have had Scientific American magazines in the house when I was a child, and got introduced to non-decimal arithmetic at a very early age (by Martin Gardner’s column in Sci Am) – mother’s milk to me… |
Rick Murray (539) 13850 posts |
So it’s not possible to scale a hundred values of 0.01… …but yet it is fine scaling a thousand 0.001s and ten 0.1s? No, sorry, this just doesn’t make sense.
You know that’s a star, right? ;)
BBC Micro / 6502 → binary. Just let’s gloss over the weird that is BCD (and why did the 6502 support that in the first place?). |
Clive Semmens (2335) 3276 posts |
But it does – all these things are recurring binaries (the equivalent of recurring decimals) in binary fractions, so (like the 1/3 and 2/3) it’s a matter of chance whether they round up or down. It actually varies with precision, too – which 1/3 and 2/3 in decimal don’t, because they’re single digits recurring. But if it was 7ths, whether they’re round up or down would depend on where you cut them off. |
Jean-Michel BRUCK (3009) 362 posts |
For confirmation (PariGP):binary(0.01),(precision 100) It is a structure of periodic terms starting from the 7th. Little game: by resizing the window we reveal the repetitive structure. It’s not easy to count with just 2 fingers :-) luckily we have 10! |
Clive Semmens (2335) 3276 posts |
Of course I do. Hitchhiker’s Guide to the Galaxy… |
Clive Semmens (2335) 3276 posts |
So you could do 100 * 0.01 exactly? |
Rick Murray (539) 13850 posts |
To get an idea of scale of Betelgeuse, I asked ChatGPT that if the sun was the size of a grape…
Uh… that’s epic….ally wrong! If we switched the sun with Betelgeuse, it would stretch to between the orbit of Mars and that of Jupiter (it pulsates). Sorry Earthlings, you’re toast.
But why is 0.01 somehow “special” in not being able to be exactly represented, while ÷10 or ×10 can be?
That makes sense, and is probably why BASIC does this – the standard FP is a mere five bytes which doesn’t leave much space for accuracy. |
Clive Semmens (2335) 3276 posts |
Because it’s a fraction, whereas *10 is an integer. Integers can be exactly represented in any base, whereas most fractions can only be approximated in most bases. Divide by ten can’t be done exactly in binary. |
Dave Higton (1515) 3534 posts |
Binary floating point can represent, with perfect accuracy, 0.5, 0.25, 0.125, 0.0625, 0.03125, etc. and numbers that are sums of the numbers it can represent perfectly accurately (with the proviso that the fractions must not be too different in magnitude, so that they all fit within the mantissa). |
Dave Higton (1515) 3534 posts |
What a pity. There are two things about maths to me: power and beauty. |
Steve Pampling (1551) 8172 posts |
Betelgeuse One that has the dubious distinction of having its name mispronounced almost every time you hear it. “Bet” “el” (for starters) The name is derived from the Arabic for giants shoulder. |
Graeme (8815) 106 posts |
Here is one example and how it may be calculated in FP to give differing results: INT(1/3 + 2/3) I haven’t tried this. My calculations assume 3 decimal point accuracy for simplicity. Method A (faster): Method B (accuracy): Method A calculates three decimal places and stops, moving on quickly. Method B must calculate four decimal places to work out if 1/3, 2/3 or addition should be rounded up. In this case 2/3 is rounded up for better accuracy but all calculations must be checked, slowing down everything. BBC BASIC needed to be fast and may have used method A. FPA/FPE/VFP have rounding and may use method B by default. Does BASIC64 use FPE/VFP? Speed versus accuracy. Could be the answer to why INT seems inconsistent on FP values? |