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 : lesson in floating point
Author | Message | ||||
crez Senior Member Joined: 24/10/2012 Location: AustraliaPosts: 152 |
try this short program in mmbasic: a=0 : b=10 : c=100 : d=1000 : e=10000 For k=1 To 10000 a=a+0.0001 b=b+0.0001 c=c+0.0001 d=d+0.0001 e=e+0.0001 Next k b=b-10 : c=c-100 : d=d-1000 : e=e-10000 Print "a";a,"b";b,"c";c,"d";d,"e";e The results are interesting. David |
||||
JohnS Guru Joined: 18/11/2011 Location: United KingdomPosts: 3804 |
In what way(s)? John |
||||
crez Senior Member Joined: 24/10/2012 Location: AustraliaPosts: 152 |
John, In a 'perfect' world, you would expect all the results to be 10000 * 0.0001, which is 1. The program above gives errors that are both positive and negative, and the error is worst when adding a small increment (0.0001) to a large number (10000). In this case it seems the sum of 10000 increments of 0.0001 is 0 This is the result of the above program: a 1.00005 b 1.00136 c 0.991821 d 1.2207 e 0 I expect that a careful examination of the floating point precision being used would explain the results. David |
||||
JohnS Guru Joined: 18/11/2011 Location: United KingdomPosts: 3804 |
We've discussed this on this forum before. It's an inherent (i.e. unavoidable) feature of computer fp (floating point). It affects x86 PCs, Macs, mainframes etc etc. If you don't want it, don't use fp! (It's why COBOL, for example, has BCD.) The underlying cause is that the range of fp is large and there are many values it cannot precisely represent i.e. there are quantisation effects. You can get a similar effect using an old-fashioned calculator if you divide a number (that's not a multiple of 3) by 3 or 9 etc then later multiply by 3 (or 9). John |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 6100 |
32 bit floating point maths has about 7 significant digits. In your example, 'e' is trying for 10 significant digits. Floating point maths is always a shock when you first see its limitations. Page 15 of the help file: Double precision numbers would be nice but they would come with big overheads. There are external chips that will do double precision if needed and I did produce a program for doing big integer maths (but very slow). Jim VK7JH MMedit  MMBasic Help |
||||
MicroBlocks Guru Joined: 12/05/2012 Location: ThailandPosts: 2209 |
I would like 32 bit integers. High precision and very fast. Of course you need both floating point and integers to cover programming needs. Microblocks. Build with logic. |
||||
crez Senior Member Joined: 24/10/2012 Location: AustraliaPosts: 152 |
I'm not complaining or suggesting there is something that needs fixing. I was caught by this and other part-time programmers like me might be caught too. In my case I was adding small energy measurements ( eg 0.001 watt-hours ) every 50 milliseconds to a daily total that was reaching 9000. It was fine at the start of the day, but by the end of the day it failed completely. One fix would be to have a second energy total that is emptied into the main one when it reaches a significant level, or every ten seconds, which would reduce the problem by two orders of magnitude. David |
||||
James_From_Canb Senior Member Joined: 19/06/2011 Location: AustraliaPosts: 265 |
Maybe you could multiply each small value by 1000 before adding it to the total, then divided the total by 1000 at the end. That would bring you back into the 7 decimal place range of accuracy for the additions. James My mind is aglow with whirling, transient nodes of thought careening through a cosmic vapor of invention. Hedley Lamarr, Blazing Saddles (1974) |
||||
crez Senior Member Joined: 24/10/2012 Location: AustraliaPosts: 152 |
James, The problem arises from the difference in magnitude between the sum and the increment. It is not the actual size of the numbers but the huge difference in size. Multiplying each small value by 1000 would increase the sum by a factor of 1000, so we would be back to square 1. I have added a second variable to hold a temporary sum. When this reaches 1 I empty it into the original sum. In this way, even when the samples get quite small, I am not adding a variable to one that is more than 10000 times its size, so the errors will be insignificant. David |
||||
isochronic Guru Joined: 21/01/2012 Location: AustraliaPosts: 689 |
I did a quick look around, it looks like Arduinos and their offshoots do the same thing (apart from the chipKit based version), maybe someone could verify that. It is a bit of a stretch in scope for the smaller platforms to go to 64 bit math I guess. |
||||
Gizmo Admin Group Joined: 05/06/2004 Location: AustraliaPosts: 5078 |
This reminds me of a discussion I had with a work mate. He was writing a game in C++ as a learning exercise, on a 32bit platform, and discovered a problem when he wanted to track the milliseconds the player had been playing. If the player was playing for too long, the counter would reset and start again, reaching the integer limit on a 32bit byte. His solution was to move to a 64 bit system! After some discussion and my reassuring him the first PC's only used 8 bits, but could still process large numbers, he changed his code to break up the number into 2 32bit bytes. Problem solved. In Crez's application, I would record the watt hours for one hour, then reset it back to zero for the next hour. At the end of the day, add all 24 hour totals to get the watt hours for the day. Glenn The best time to plant a tree was twenty years ago, the second best time is right now. JAQ |
||||
isochronic Guru Joined: 21/01/2012 Location: AustraliaPosts: 689 |
As it happens, I am almost finished a small fortran-ish interpreter for pic32mx that has double precision datatypes and so on. It is (deliberately) whimsical in that number-crunching with an interpreter tends to be a complete contradiction !! However... I thought I would run an approximation of the example above, out of curiosity. It looks ok, the double precision gives you about 12 significant figures, it is a little slow on a mx1 but a '795 should be nice. Now I need a 64-bit analog-to-digital converter PRINT "Hello world" ; INTEGER*4 a; DOUBLE b, c, d, e, f, g ; b = 0.00000000000 ; c = 10.00000000000 ; d = 100.00000000000 ; e = 1000.00000000000 ; f = 10000.00000000000 ; g = 0.00010000000 ; DO 10 a = 1,10000 ; b = b + g ; c = c + g ; d = d + g ; e = e + g ; 10 f = f + g ; PRINT "b = ", b ; PRINT "c = ", c ; PRINT "d = ", d ; PRINT "e = ", e ; PRINT "f = ", f ; PRINT "Finished" ; END; \ Hello world b = 1.0000000 c = 11.0000000 d = 101.0000000 e = 1001.0000000 f = 10001.0000000 Finished |
||||
Print this page |