Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.
|
Forum Index : Microcontroller and PC projects : [MMBasic]Mathematical Precision
Author | Message | ||||
G8JCF Guru Joined: 15/05/2014 Location: United KingdomPosts: 676 |
In the "MMBasic Language Manual" in the implementation characteristics is says "Numbers are stored and manipulated as single precision floating point numbers. The maximum number that can be represented is 3.40282347e+38 and the minimum is 1.17549435e-38" and shows 9 digit precision. When I run MMBasic itself, either in DOS or on a Micromite the most I ever see is 6 digits of precision, try PRINT1/7 for example Can someone tell me please if 6 digit precision is correct, and/or is there a way to see more precision ? Reason for asking is that I'm trying to do calculations in MHz to 1 Hz precision, eg LET FREQ=12345678 'Frequency in Hz PRINT FREQ 1.23457e+07 'Displayed in response to the PRINT statement Many thanks Peter The only Konstant is Change |
||||
JohnS Guru Joined: 18/11/2011 Location: United KingdomPosts: 3678 |
9 digits would be about 30 bits (*) and for PIC32 single precision fp of 32 bits that sounds unlikely. But some MHz meaning millions would only be 7 digits (or so) and should be OK. I think it just uses the format that's more or less native to the chip so the Mchp doc should give details. PRINT will be using some default format / rounding / etc. Is STR$ or whatever that new thing is any good? Or write a test program but with a bit of thought LOL (*) because 10^3 is about 2^10 and 10^9 is about 2^30 John |
||||
G8JCF Guru Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi John Thanks for the reply, I need to deal with MHz up to 40,000,000 hence 8 digits. I've come across TassyJim's BigInt program so I might spend some time optimising that for improved speed. I was just curious that since the manual showed 9 digit precision that perhaps there was some really clever trick which I just hadn't found - I tried STR$ but to no avail. 2^31 is 2147483648 is 9 digits, but there doesn't seem to be a LONG or INT32 data type in MMBasic, if there was then by judicious scaling one can do all integer arithmetic and maintain high precision. Anyway, thanks for taking the time to explain. 73 Peter The only Konstant is Change |
||||
JohnS Guru Joined: 18/11/2011 Location: United KingdomPosts: 3678 |
Apparently always stored in one way, single fl.pt. Might manage 4E7 without loss of precision. Try it... John |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 5923 |
You can use FORMAT$ to prevent the display from defaulting to exponential format. FREQ=12345678 'Frequency in Hz
PRINT FREQ print format$(freq,"%9.0f") You will still be limited by the precision of 32 bit floating point. I would split the number into Hz, kHz, MHz if possible and then put the strings together for display. The biggest integer is 16777100 so if you can live with 10Hz precision 40 MHz is achievable without splitting numbers although you will have to watch any maths which could easily loose precision. My BigInts program is very slow and one day I will try to produce a faster version probably by working in thousands instead of single digits. Jim VK7JH MMedit MMBasic Help |
||||
robert.rozee Guru Joined: 31/12/2012 Location: New ZealandPosts: 2294 |
this could be an interesting point to ask a question of geoff: how difficult would it be to recompile the various versions of mmbasic to use turbo pascal style 6-byte reals? this would give 11-digits of decimal precision, as well as encompassing 36-bit integers. internally one would then do conversions to handle as 8-byte ieee reals within the interpreter. or one could go for 8-byte ieee reals throughout, for 15 digits of decimal precision and to encompass 48-bit integers. this is more of a hypothetical question, as aside from the type change, i'm quite sure the associated C libraries would likely blow out the flash budgets on both micromite and maximite. another interesting option would be to add a purely integer data type, where an integer is specified with a "%" sign as the last character of the variable name, ie: ABC% = 12345
this would then allow for 32-bit integers, or one could be extravagant and go for 48 or 64-bits with 6 or 8 bytes per integer. rob :-) |
||||
G8JCF Guru Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi 32 bit Unsigned Integers would be really convenient especially for a 32 bit processor. I don't think the FORMAT$ keyword exists for the Micromite version of MMBasic. I've started writing a BigInteger library of my own, and thus far have coded up AddBig and MulBig (these are the easy ones of course !), DivBig is the really tough one, followed by SubBig. Here's AddBig to give you all a taste of the methods I'm using '*********************************************************** *
'Add 2 x 12 digit integers. Result is a 13 Digit integer ' '*********************************************************** * Function AddBig$(a$,b$) Local A(12),B(12),C(13),I,J,C$(1) length 13 'Split A$ into A() J=0 for I=Len(a$) to 1 step -1 A(J)=val(Mid$(A$,I,1)) J=J+1 next I 'Split B$ into B() J=0 for I=Len(B$) to 1 step -1 B(J)=val(Mid$(B$,I,1)) J=J+1 next I Local Carry Carry=0 'Add each element of A() to B() result into c() For I = 0 to 11 C(I)=A(I) + B(I) + Carry if C(I)>9 then Carry=1 C(I)=c(I)-10 else Carry=0 endif Next I C(12)=Carry For I=12 to 0 step -1 c$(1)=c$(1) + str$(c(I)) next AddBig$=TrimLZero$(C$(1)) End Function Function TrimLZero$(A$) local I for I= 1 to Len(A$) If Mid$(A$,I,1)<>"0" then TrimLZero$= Mid$(A$,I) exit function endif next I TrimLZero$="0" end function Thanks for all the ideas The only Konstant is Change |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 5923 |
The micromite alternative for the FORMAT$ command is STR$ Not a full equivalent by has the necessary options for this purpose. Jim VK7JH MMedit MMBasic Help |
||||
G8JCF Guru Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi TassyJim Further to your comments I've coded up MulBig to work in chunks of 3 decimal digits at a time which as you suggested makes it much, much faster. 3 Digits at a time is the maximum number which preserves full precision across the full range of numbers, eg 99,999,999 x 9,999,999,999. Anyway in case someone else needs long precision Multiplication here's the source ' 'Returns A$ x B$ as a string 'Usage A$="12345678", B$="1234" 'C$=MulBCD$(A$,B$) 'This algorithm works on Triplets of digits ' Function MulBCD$(A$, B$) Local Cy, I, J, K, L, M, N 'Arrays into which we will unpack the BCD numbers Local A1(4), B1(4) 'Arrays for the answers Local D(12), C1(8) Local C$(1) Length 3, C2$(1) Length 24 Local A2$(1) Length 12, B2$(1) Length 12 M = Len(A$) N = Len(B$) 'Optimise the speed by making sure the Multiplier is the shorter of the '2 arguments If N > M Then A2$(1) = B$ B2$(1) = A$ Else A2$(1) = A$ B2$(1) = B$ EndIf M = Len(A2$(1)) N = Len(B2$(1)) 'Are the numbers in multiples of 3 digits J = 0 If M Mod 3 <> 0 Then For I = 3 To 12 Step 3 If M < I Then J = I - M M = I Exit For EndIf Next I EndIf A2$(1) = String$(J, "0") + A2$(1) 'Are the arguments in multiples of 3 digits J = 0 If N Mod 3 <> 0 Then For I = 3 To 12 Step 3 If N < I Then J = I - N N = I Exit For EndIf Next I EndIf B2$(1) = String$(J, "0") + B2$(1) 'Unpack the Numbers from String into Numeric array J = 0 For I = M - 2 To 1 Step -3 A1(J) = Val(Mid$(A2$(1), I, 3)) J = J + 1 Next I J = 0 For I = N - 2 To 1 Step -3 B1(J) = Val(Mid$(B2$(1), I, 3)) J = J + 1 Next I 'Now Do the Long Multiplication of the two numbers 'Set Units first L = 0 K = 0 For I = 0 To M \ 3 Step 1 Cy = 0 For J = 0 To N \ 3 Step 1 C1(J) = (A1(I) * B1(J)) + Cy If C1(J) > 999 Then Cy = C1(J) \ 1000 C1(J) = C1(J) Mod 1000 Else Cy = 0 EndIf Next J 'Add the partial result to the overall total 'Add each element of C() to D() result into D() Cy = 0 For J = 0 To 23 \ 3 K = J + L D(K) = D(K) + C1(J) + Cy If D(K) > 999 Then Cy = D(K) \ 1000 '100 D(K) = D(K) Mod 1000 '- 1000 Else Cy = 0 EndIf Next J L = L + 1 Next I 'Build result string For I = 23 \ 3 To 0 Step -1 C$(1)="" If D(I) < 100 Then C$(1)="0" If D(I) < 10 then C$(1)="00" C2$(1) = C2$(1) + C$(1)+(Str$(D(I))) Next I C2$(1) = TrimL$(C2$(1), "0") MulBCD$=C2$(1) End Function I hope someone finds this useful ! 73 Peter (GM8JCF) The only Konstant is Change |
||||
G8JCF Guru Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Sorry, I forgot to include TrimL$ '==================================== ' 'Remove leading CHARacters from a string 'If char$ is ommitted or blank then trim ZEROES ' Function TrimL$(A$, Char$) Local I, CH$(1) Length 1 ch$(1) = Left$(Char$, 1) If ch$(1) = "" Then ch$(1) = "0" For I = 1 To Len(A$) If Mid$(A$, I, 1) <> "0" Then TrimL$ = Mid$(A$, I) Exit Function EndIf Next I TrimL$ = CH$(1) End Function The only Konstant is Change |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 5923 |
Thanks for that Peter. It was something I was going to have to do soon. Jim VK7JH MMedit MMBasic Help |
||||
Print this page |