![]() |
Forum Index : Microcontroller and PC projects : [MMBasic] 7 Digit Numeric Precision
Author | Message | ||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi Last Friday, @JohnS, showed me how to get at the hidden 7th digit of MMBasic numeric vars - thank you @JohnS. I thought that @JohnS' insight was very worthwhile sharing with a wider audience. Try this Print PI MMBasic will display 3.14159 Now try this instead Print int(pi);".";str$((pi-int(pi))*1000000) MMBasic will display 3.141593 ie 7 digit precision And here's @JohnS' original proof of the 7th digit and how to get at it. x = 999999 + 999999 IF x < 1000000 THEN PRINT x ELSE PRINT x \ 1000000; PRINT STR$(x - (x \ 1000000) * 1000000 ) ENDIF Run it and MMBasic will print 1999998 That's 7 digits, and MMBasic hasn't switched into exponential notation, eg 2.00000e+06 73 Peter The only Konstant is Change |
||||
BobD![]() Guru ![]() Joined: 07/12/2011 Location: AustraliaPosts: 935 |
It's only small stuff but as well as 7 digits you are trying to get speed and it all helps. I seem to recall that you can knock out the semi-colons (;) in the above and still get the desired result. Print int(pi)"."str$((pi-int(pi))*1000000) However, you can't do that for one at the end of a line. Bob |
||||
JohnS Guru ![]() Joined: 18/11/2011 Location: United KingdomPosts: 3998 |
All I did was to note the 16777100 and suspect there'd be 7 (and a little bit more) digits. I've just made a little test using the PIC32/MIPS32 C32 (gcc) and it looks like the biggest integer that's exact is 16777215 (or 16777216 but that happens to be an exact power of 2), i.e. 0xffffff or 24 bits. I'm cheating really, as I've used a considerable number of floating point systems over the years and though they tend to vary somewhat they're also much alike - and you pick things like this up. Occasionally it's actually useful! You might like to read up on things like "machine epsilon"... John |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
@JohnS Great insight, all those years at the FP coalface :). BTW, I wouldn't call it cheating, you just a very good programmer's nose. Anyway, thanks to your insight, I will be able to release an arbitrary precision arithmetic library which should run reasonable quickly, eg less than 2 mS for an 18 digit add/sub @ CPU 48, and it does include Mul & Div because moving/marshalling the numbers between sextet BCD packing, ie 6 decimal digits/numeric var and triplet BCD packing, ie 3 decimal digits/numeric var is very simple. I've had the triplet BCD packing arith. lib done and dusted for over a month now but was trying to squeeze more performance out for the AD9850Controller project. Take care Peter The only Konstant is Change |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
@BobD Thanks for that tip. The only downside of using undocumented features such as this print trick, is that some future version of MMBasic might tighten up on the Syntax checking and break the code, but still worth knowing. Thanks Peter The only Konstant is Change |
||||
BobD![]() Guru ![]() Joined: 07/12/2011 Location: AustraliaPosts: 935 |
It was a tip announced by GeoffG recently. Sorry I can't refer back to his post. Searching this board can be difficult without a good key word. |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi This might prove useful for displaying large numbers without swithing into exponent notation - useful for frequency displays mainly I would imagine. Best precision is 7 digits, for 10 MHz and above, zeroes are appended to the figure as appropriate. I needed this for the AD9850 controller frequency display but it might be useful elsewhere. (I just hope there's not a bug lurking in there !) 73 Peter '========================================================= ' ' Function which extends the functionality of the native ' STR$() function ' ' If Number < 1,000,000 then 6 digit precision using native STR$() ' If 999,999 < Number < 9,999,998 then 7 digit precision result ' If 9,999,998 < Number < 100,000,00 then 7 digit precision result with 1 Zero appended ' If 99,999,999 < Number < 999,999,968 then 7 digit precision with 2 zeroes appended ' If 999,999,967 < Number then 6 digit precision using native STR$() ' ' FUNCTION StrEx$(Number,M,N,C$) 'Is Number small enough to use normal STR$ ? IF Number < 1000000 THEN StrEx$=STR$(Number,M,N,C$) EXIT FUNCTION ENDIF 'We have a big enough number such that 7 digit precion is useful IF C$="" THEN C$="0" IF M<1 THEN M=6 IF N<1 THEN N=0 Exponent=VAL(RIGHT$(STR$(Number),2)) IF Number<9999999 THEN StrEx$=MID$(STR$((Number)\1e6)+STR$(Number-((Number)\1e6)*1e 6,6,N,C$),1,7) EXIT FUNCTION ENDIF IF Number<100000000 THEN '99999999 THEN StrEx$=MID$(STR$((Number+5)\1e6)+STR$(Number+5-((Number+5)\1 e6)*1e6,M,N,C$),1,7)+STRING$(Exponent-6,"0") EXIT FUNCTION ENDIF 'It should be 1000,000,000 but MMBasic errors out with "Number is too large" IF Number<999999968 THEN StrEx$=MID$(STR$((Number+50)\1e6)+STR$(Number+50-((Number+50 )\1e6)*1e6,M,N,C$),1,7)+STRING$(Exponent-6,"0") EXIT FUNCTION ENDIF 'Just return it in exponent form StrEx$=STR$(Number) END FUNCTION The only Konstant is Change |
||||
TassyJim![]() Guru ![]() Joined: 07/08/2011 Location: AustraliaPosts: 6220 |
You can use STR$ to show the full precision on the micromite. (Maximites use FORMAT for similar results) print PI
print str$(PI, 2,4) print str$(PI, 2,5) print str$(PI, 2,6) print str$(PI, 2,7);" We have gone over the true precision level of 32bit here" Jim VK7JH MMedit |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi Jim I tried this as part of the investigations but print str$(PI, 2,6) produces the incorrect result, it should be 3.141593 but instead it produces 3.141592 Print int(pi);".";str$((pi-int(pi))*1000000) produces the correct answer of 3.141593 PI (according to my HP35s) is 3.14159265359, so to 6 significant places that should be 3.141593. So is there a bug in STR$(), ie not rounding properly when the number of decimal places is specified at 6 ? It's fascinating where the quest for greater arithmetic precision is taking the conversation - I've discovered (re) a whole world of stuff I'd forgotten since I was first at University 40 years ago ! 73 Peter The only Konstant is Change |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
PS, does FORMAT$ on a MaxiMite give the correct result of 3.141593 ? The only Konstant is Change |
||||
TassyJim![]() Guru ![]() Joined: 07/08/2011 Location: AustraliaPosts: 6220 |
Peter, Due to the limit of 32 bit precision, the maximite value for PI ends up as 3.1415925 This unfortunately (for PI perfectionists) results in it getting rounded to 3.141592 rather than 3.141593 which is the more correct value. It is all due to 32bit floating point limitations. If the maximite used 64bit floating point, there would still be limitations and strange results. You can test the maximite syntax using DOS MMBasic. A while ago when I did some testing I did find that there were differences between maximite and DOS when you pushed the boundaries. This was probably due to the different C compilers used. Jim VK7JH MMedit |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi Jim Print format$(PI,"%f") in MMBasic for DOS produces the correct answer of 3.141593 So it looks (to me) that STR$() (uMIte) may have a bug in that STR$() (uMIte) because it is not rounding correctly. interestingly, in uMite, do A=1.2345675 Print STR$(A,2,6) and the correct answer is printed, ie 1.234568 Perhaps Geoff knew about these inconsistencies when he chose to limit MMBasic's PRINT statement to output 6 digit precision ? Puzzled. Peter The only Konstant is Change |
||||
TassyJim![]() Guru ![]() Joined: 07/08/2011 Location: AustraliaPosts: 6220 |
Try this code on the different platforms: PRINT MM.DEVICE$
IF MM.DEVICE$="Micromite" THEN PRINT PI PRINT STR$(PI, 2,4) PRINT STR$(PI, 2,5) PRINT STR$(PI, 2,6) PRINT STR$(PI, 2,7) PRINT 3.1415926 PRINT STR$(3.1415926, 2,4) PRINT STR$(3.1415926, 2,5) PRINT STR$(3.1415926, 2,6) PRINT STR$(3.1415926, 2,7) PRINT 3.14159265358979 PRINT STR$(3.14159265358979, 2,4) PRINT STR$(3.14159265358979, 2,5) PRINT STR$(3.14159265358979, 2,6) PRINT STR$(3.14159265358979, 2,7) PRINT 3.14159265358979323846 PRINT STR$(3.14159265358979323846, 2,4) PRINT STR$(3.14159265358979323846, 2,5) PRINT STR$(3.14159265358979323846, 2,6) PRINT STR$(3.14159265358979323846, 2,7) ELSE PRINT PI PRINT FORMAT$(PI, "%7.4f") PRINT FORMAT$(PI, "%8.5f") PRINT FORMAT$(PI, "%9.6f") PRINT FORMAT$(PI, "%10.7f") PRINT 3.1415926 PRINT FORMAT$(3.1415926, "%7.4f") PRINT FORMAT$(3.1415926, "%8.5f") PRINT FORMAT$(3.1415926, "%9.6f") PRINT FORMAT$(3.1415926, "%10.7f") PRINT 3.14159265358979 PRINT FORMAT$(3.14159265358979, "%7.4f") PRINT FORMAT$(3.14159265358979, "%8.5f") PRINT FORMAT$(3.14159265358979, "%9.6f") PRINT FORMAT$(3.14159265358979, "%10.7f") PRINT 3.14159265358979323846 PRINT FORMAT$(3.14159265358979323846, "%7.4f") PRINT FORMAT$(3.14159265358979323846, "%8.5f") PRINT FORMAT$(3.14159265358979323846, "%9.6f") PRINT FORMAT$(3.14159265358979323846, "%10.7f") ENDIF 'MicroMite: ' '// Return the value of Pi. Thanks to Alan Williams for the contribution 'void fun_pi(void) { ' fret = (float)3.1415926; '} ' 'Maximite: ' '// Return the value of Pi. Thanks to Alan Williams for the contribution 'void fun_pi(void) { ' fret = (float)3.14159265358979323846; '} ' The results are: Even though the Maximite and MicroMite have different values for PI, the output is identical for both the PIC32 versions. The DOS version is compiled with an entirely different compiler which is why you see differences. It is always dangerous when you are pushing the limits of precision. The plain PRINT x is playing save and gives a reasonably true indication of the available precision. In all cases, if you try and enter a very long number, strange things happen. Jim VK7JH MMedit |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
So Jim I understand your point I think. From your Output prints, it seems to me that only the DOS MMBasic is carrying out proper rounding from 6 digit to 4 digit precision, all the other versions seem to chop off digits when using STR$(). What are the takeaway(s) from this then ? Are there any ? If so on the Maximite & uMite could these be they ? 1) Avoid STR$() with N > 5 2) If you need 7 digit precision then use something like Print int(pi);".";str$((pi-int(pi))*1000000) 3) Avoid 6 decimal places if using STR$() 73 Peter The only Konstant is Change |
||||
JohnS Guru ![]() Joined: 18/11/2011 Location: United KingdomPosts: 3998 |
Some of those may be hitting the issue of precision, roughly meaning the number of significant digits that can be represented (stored). There might also be an issue with the conversion from the program source into the internal (binary, if you like) representation. John |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi John, Jim Could it be that the DOS version of MMDos is using the H/W FPU built into Intel processors to do its SINGLE floating point arithmetic ? If so then I think that might explain why MMBasic on DOS is more correct than the other platforms since the Intel FPU has been really, really well designed and complies completely in all respects with the IEEE specifications for floating point implementations. (I mean given the billions of CPU's Intel has sold, if their FPU was deficient in any way (yes there was that Pentium bug which was the result of 5 lookup table entries in the silicon being incorrect !) we would have heard all about it, and they would have fixed it.) Having said all that, I could be completely barking up the wrong tree of course. I suppose the IEEE must have a test suite of operations with values which any system which purports to implement their floating point standard - IEEE 754 - must be able to demonstrate compliance with (MMBasic doesn't make that claim afaik)? 73 Peter The only Konstant is Change |
||||
JohnS Guru ![]() Joined: 18/11/2011 Location: United KingdomPosts: 3998 |
If I could run the DOS version I could find out! But it's an NT windows console app by the looks of it and Linux WINE doesn't seem happy with it. I can run the Linux MMBasic OK and that appears to have the same fp as the PIC32 from a not very long test. Could easily be using the IEEE754 stuff which as you say is generally excellent (I still have the full spec, how sad is that). Oh, the CPU bug... I recall the hassle Intel got but they did eventually offer to swap the chips (I was working for a computer maker at the time). Don't suppose you know G4CLI? John |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi John Unfortunately I haven't yet come across G4CLI - he's only 7 months older than me ! Why ? I operate on 40m and 20m with 100W from an IC-7200, but since I've gotten hold of these uMites, I've not been on the air at all ![]() Have U got VMWare ? If so U could then run Windows in a VM under LINUX with XP (XP is pretty much free these days) and that way try out various Windows only s/w perhaps. I do it the other way round, I run Windows 64 with 8 GB of RAM and then have Debian VMs under VMWare (all free stuff). Did U have to implement an IEE754 library ? Peter The only Konstant is Change |
||||
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |