Re: [Tutor] the binary math "wall"
>From the virtual desk of Lowell Tackett --- On Tue, 4/20/10, Steven D'Aprano wrote: > From: Steven D'Aprano > Subject: Re: [Tutor] the binary math "wall" > To: tutor@python.org > Date: Tuesday, April 20, 2010, 7:39 PM > On Wed, 21 Apr 2010 02:58:06 am > Lowell Tackett wrote: > > I'm running headlong into the dilemma of binary math > representation, > with game-ending consequences, e.g.: > > >>> 0.15 > But if you really need D.MMSS floats, then something like > this should be > a good start.: > > def dms2deg(f): > """Convert a floating point number formatted > as D.MMSS > into degrees. > """ > mmss, d = math.modf(f) > assert d == int(f) > if mmss >= 0.60: > raise ValueError( > 'bad fractional part, expected > < .60 but got %f' % mmss) > mmss *= 100 > m = round(mmss) > if m >= 60: > raise ValueError('bad minutes, > expected < 60 but got %d' % m) > s = round((mmss - m)*100, 8) > if not 0 <= s < 60.0: > raise ValueError('bad seconds, > expected < 60.0 but got %f' % s) > return d + m/60.0 + s/3600.0 > > > >>> dms2deg(18.15) > 18.25 > >>> dms2deg(18.1515) > 18.2541666 > > Haven't gone away...I'm having a blast dissecting (parsing[?]-is that the right word?) your script snippet! Got a big pot of coffee fueling the effort. > > Note though that this still fails with some valid input. I > will leave > fixing it as an exercise (or I might work on it later, time > > permitting). Got the hint...I'm gonna pick up this challenge. This effort is taking me in endless tangents (all productive)-I'll be back sometime [soon] with my efforts and results. Thanks for your [own] time and effort. > > > -- > Steven D'Aprano > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > http://mail.python.org/mailman/listinfo/tutor > ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
Steven D'Aprano wrote: On Thu, 22 Apr 2010 01:37:35 am Lowell Tackett wrote: Were I to accumulate many of these "legs" into perhaps a 15 mile traverse-accumulating little computer errors along the way-the end result could be catastrophically wrong. YES!!! And just by being aware of this potential problem, you are better off than 90% of programmers who are blithely unaware that floats are not real numbers. Absolutely. But "catastrophically wrong" has to be defined, and analyzed. If each of these measurements is of 100 feet, measured to an accuracy of .0001 feet, and you add up the measurements in Python floats, you'll be adding 750 measurements, and your human error could accumulate to as much as .07 feet. The same 750 floating point ads, each to 15 digits of quantization accuracy (thanks for the correction, it isn't 18) will give a maximum "computer error" of maybe .1 feet. The human error is much larger than the computer error. No results can be counted on without some analysis of both sources of error. Occasionally, the "computer error" will exceed the human, and that depends on the calculations you do on your measurements. HTH, DaveA ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
On Thu, 22 Apr 2010 01:37:35 am Lowell Tackett wrote: > Recalling (from a brief foray into college Chem.) that a result could > not be displayed with precision greater than the least precise > component that bore [the result]. So, yes, I could accept my input > as the arbitrator of accuracy. Unfortunately, you can't distinguish the number of supplied digits of accuracy from a float. Given as floats, all of the following are identical: 0.1 0.1 0.09 0.11 as are these two: 0.08 0.099985 Perhaps you should look at the Decimal class, not necessarily to use it, but to see what they do. For instance, you create a Decimal with a string, not a float: >>> from decimal import Decimal >>> Decimal('0.1') Decimal("0.1") >>> Decimal('0.1') Decimal("0.1") which allows you to distinguish the number of digits of precision. > A scenario: > > Calculating the coordinates of a forward station from a given base > station would require [perhaps] the bearing (an angle from north, > say) and distance from hither to there. Calculating the north > coordinate would set up this relationship, e.g.: > > cos(3° 22' 49.6") x 415.9207'(Hyp) = adjacent side(North) > > My first requirement, and this is the struggle I (we) are now engaged > in, is to convert my bearing angle (3° 22' 49.6") to decimal degrees, > such that I can assign its' proper cosine value. This is MUCH MUCH MUCH easier than trying to deal with DMS as a float. Your data already separates the parts for you, so it is just a matter of: >>> d = 3 + 22/60.0 + 49.2/3600.0 >>> import math >>> angle = math.radians(d) >>> math.cos(angle) 0.9982601259166638 Then the only problem you have is whether or not the formula you are using is numerically stable, or whether it is subject to catastrophically growing errors. I hope we're not frightening you off here. For nearly anything people are going to want to do, their input data will be in single-precision. One of the simplest things they can do to improve the accuracy of floating point calculations is to do their intermediate calculations in double-precision. The good news is, Python floats are already in double-precision. For most modern systems, single-precision floats have 24 binary digits of precision (approximately 6 decimal digits) and double-precision floats have 53 binary digits (15 decimal) of precision. More than sufficient for dealing with an angle measured to a tenth of a second. Some resources for you to read: http://en.wikipedia.org/wiki/Floating_point http://www.cs.princeton.edu/introcs/91float/ http://www.cs.berkeley.edu/~wkahan/ http://docs.sun.com/source/806-3568/ncg_goldberg.html > Were I to accumulate many of these "legs" into perhaps a 15 mile > traverse-accumulating little computer errors along the way-the end > result could be catastrophically wrong. YES!!! And just by being aware of this potential problem, you are better off than 90% of programmers who are blithely unaware that floats are not real numbers. -- Steven D'Aprano ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
Lowell Tackett wrote: > >From the virtual desk of Lowell Tackett > > > > --- On Wed, 4/21/10, Dave Angel wrote: > > >> From: Dave Angel >> Subject: Re: [Tutor] the binary math "wall" >> To: "Lowell Tackett" >> Cc: tutor@python.org, "Steven D'Aprano" >> Date: Wednesday, April 21, 2010, 12:31 PM >> Lowell Tackett wrote: >> >>> From the virtual desk of Lowell Tackett >>> >>> >>> > *Whole buncha stuff snipped* > >> Anyway, in your case binary floating point is >> irrelevant. You've got to do your own error analysis >> of every step in the calculation to understand how much you >> can trust the result. And it's unlikely the "errors" >> in the math package will be the limiting factor. >> >> DaveA >> >> >> > > The sense of all this is beginning to emerge (perhaps). Emile has just made > a good point, simply convert all to whole values (using a subset algorithm > that keeps track of the number of decimal "slips" that have accumulated-for > later reconversion), crunch merrily away, and get good results. > > Maybe that's valid, maybe not. But I do know that this much is happening; > I'm now beginning to quantify very novel and wide ranging ideas way outside > "the box" I'd originally approached the challenge with. That's the bigger > value of this entire exercise, I think. Naively thinking that the computer > was benign and forgiving, I'd first opted for the most transparent and 'at > hand' solutions...there was much to be learned. > > I know a coupla things now...I'm gonna solve this dilemma, and largely with > the help of all who've weighed in. The solution may ultimately not be close > to what has been suggested, but isn't that the fun of it all? > > Secondly, there has accumulated [here] quite an array of novel (to me) > thinking that I'm going to return to more and more as I tackle other, > different projects, for the pedagogical value that will linger. > Check python help docs for module decimal, there you have an example on how to code a sin() and cos() functions with arbitrary precision (depends on your context precision) using decimal values. Might be what you are looking for (of course you would have to express your angles in radians throughout all of your calculations and if needed convert at the end. HTH ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
>From the virtual desk of Lowell Tackett --- On Wed, 4/21/10, Dave Angel wrote: > From: Dave Angel > Subject: Re: [Tutor] the binary math "wall" > To: "Lowell Tackett" > Cc: tutor@python.org, "Steven D'Aprano" > Date: Wednesday, April 21, 2010, 12:31 PM > Lowell Tackett wrote: > > From the virtual desk of Lowell Tackett > > > > *Whole buncha stuff snipped* > > Anyway, in your case binary floating point is > irrelevant. You've got to do your own error analysis > of every step in the calculation to understand how much you > can trust the result. And it's unlikely the "errors" > in the math package will be the limiting factor. > > DaveA > > The sense of all this is beginning to emerge (perhaps). Emile has just made a good point, simply convert all to whole values (using a subset algorithm that keeps track of the number of decimal "slips" that have accumulated-for later reconversion), crunch merrily away, and get good results. Maybe that's valid, maybe not. But I do know that this much is happening; I'm now beginning to quantify very novel and wide ranging ideas way outside "the box" I'd originally approached the challenge with. That's the bigger value of this entire exercise, I think. Naively thinking that the computer was benign and forgiving, I'd first opted for the most transparent and 'at hand' solutions...there was much to be learned. I know a coupla things now...I'm gonna solve this dilemma, and largely with the help of all who've weighed in. The solution may ultimately not be close to what has been suggested, but isn't that the fun of it all? Secondly, there has accumulated [here] quite an array of novel (to me) thinking that I'm going to return to more and more as I tackle other, different projects, for the pedagogical value that will linger. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
Lowell Tackett wrote: From the virtual desk of Lowell Tackett --- On Wed, 4/21/10, Dave Angel wrote: From: Dave Angel Subject: Re: [Tutor] the binary math "wall" To: "Lowell Tackett" Cc: tutor@python.org, "Steven D'Aprano" Date: Wednesday, April 21, 2010, 6:46 AM Lowell Tackett wrote: --- On Tue, 4/20/10, Steven D'Aprano wrote: From: Steven D'Aprano The simplest, roughest way...hit them with a hammer: round(18.15*100) == 1815 True ...when I tried...: Python 2.5.1 (r251:54863, Oct 14 2007, 12:51:35) [GCC 3.4.1 (Mandrakelinux 10.1 3.4.1-4mdk)] on linux2 Type "help", "copyright", "credits" or "license" for more information. round(18.15)*100 == 1815 False But you typed it differently than Steven. He had round(18.15*100), and you used round(18.15)*100 As soon as I'd posted my answer I realized this mistake. Very different. His point boils down to comparing integers, and when you have dubious values, round them to an integer before comparing. I have my doubts, since in this case it would lead to bigger sloppiness than necessary. round(18.154 *100) == 1815 probably isn't what you'd want. So let me ask again, are all angles a whole number of seconds? Or can you make some assumption about how accurate they need to be when first input (like tenths of a second, or whatever)? If so use an integer as follows: val = rounddegrees*60)+minutes)*60) + seconds)*10) The 10 above is assuming that tenths of a second are your quantization. HTH DaveA Recalling (from a brief foray into college Chem.) that a result could not be displayed with precision greater than the least precise component that bore [the result]. So, yes, I could accept my input as the arbitrator of accuracy. A scenario: Calculating the coordinates of a forward station from a given base station would require [perhaps] the bearing (an angle from north, say) and distance from hither to there. Calculating the north coordinate would set up this relationship, e.g.: cos(3° 22' 49.6") x 415.9207'(Hyp) = adjacent side(North) My first requirement, and this is the struggle I (we) are now engaged in, is to convert my bearing angle (3° 22' 49.6") to decimal degrees, such that I can assign its' proper cosine value. Now, I am multiplying these two very refined values (yes, the distance really is honed down to 10,000'ths of a foot-that's normal in surveying data); within the bowels of the computer's blackboard scratch-pad, I cannot allow errors to evolve and emerge. Were I to accumulate many of these "legs" into perhaps a 15 mile traverse-accumulating little computer errors along the way-the end result could be catastrophically wrong. (Recall that in the great India Survey in the 1800's, Waugh got the elevation of Mt. Everest wrong by almost 30' feet for just this exact same reason.) In surveying, we have a saying, "Measure with a micrometer, mark with chalk, cut with an axe". Accuracy [in math] is a sacred tenet. So, I am setting my self very high standards of accuracy, simply because those are the standards imposed by the project I am adapting, and I can require nothing less of my finished project. If you're trying to be accurate when calling cos, why are you using degrees? The cosine function takes an angle in radians. So what you need is a method to convert from deg/min/sec to radians. And once you have to call trig, you can throw out all the other nonsense about getting exact values. Trig functions don't take arbitrary number units. They don't take decimals, and they don't take fractions. They take double-precision floats. Perhaps you don't realize the amount of this quantization error we've been talking about. The double type is 64bits in size, and contains the equivalent of about 18 decimal digits of precision. (Assuming common modern architectures, of course) Your angle is specified to about 5 digits of precision, and the distance to 7. So it would take a VERY large number of typical calculations for errors in the 18th place to accumulate far enough to affect those. The real problem, and one that we can't solve for you, and neither can Python, is that it's easy to do calculations starting with 8 digits of accuracy, and the result be only useful to 3 or 4. For example, simply subtract two very close numbers, and use the result as though it were meaningful. I once had a real customer send us a letter asking about the math precision of a calculation he was doing. I had written the math microcode of the machine he was using (from add and subtract, up to trigs and logs, I
Re: [Tutor] the binary math "wall"
On 4/21/2010 8:37 AM Lowell Tackett said... So, I am setting my self very high standards of accuracy, simply because those are the standards imposed by the project I am adapting, and I can require nothing less of my finished project. Then keep the source data in tenths (or 100ths or 1000ths) as whole numbers and only convert upon presentation and your calculations will always be accurate for the source measurements taken. In accounting systems I've always keep dollar amounts in whole pennies for this same reason. Emile ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
>From the virtual desk of Lowell Tackett --- On Wed, 4/21/10, Dave Angel wrote: > From: Dave Angel > Subject: Re: [Tutor] the binary math "wall" > To: "Lowell Tackett" > Cc: tutor@python.org, "Steven D'Aprano" > Date: Wednesday, April 21, 2010, 6:46 AM > > > Lowell Tackett wrote: > > --- On Tue, 4/20/10, Steven D'Aprano > wrote: > > > > > >> From: Steven D'Aprano > >> > >> > >> The simplest, roughest way...hit them with a > >> hammer: > >> > >>>>> round(18.15*100) == 1815 > >>>>> > > >> True > >> > > > > ...when I tried...: > > > > Python 2.5.1 (r251:54863, Oct 14 2007, 12:51:35) > > [GCC 3.4.1 (Mandrakelinux 10.1 3.4.1-4mdk)] on linux2 > > Type "help", "copyright", "credits" or "license" for > more information. > > > >>>> round(18.15)*100 == 1815 > >>>> > > False > > > > > But you typed it differently than Steven. He > had round(18.15*100), and you used > round(18.15)*100 As soon as I'd posted my answer I realized this mistake. > > Very different. His point boils down to > comparing integers, and when you have dubious values, round > them to an integer before comparing. I have my doubts, > since in this case it would lead to bigger sloppiness than > necessary. > > round(18.154 *100) == 1815 > > probably isn't what you'd want. > > So let me ask again, are all angles a whole number of > seconds? Or can you make some assumption about how > accurate they need to be when first input (like tenths of a > second, or whatever)? If so use an integer as > follows: > > val = rounddegrees*60)+minutes)*60) + > seconds)*10) > > The 10 above is assuming that tenths of a second are your > quantization. > > HTH > DaveA > > Recalling (from a brief foray into college Chem.) that a result could not be displayed with precision greater than the least precise component that bore [the result]. So, yes, I could accept my input as the arbitrator of accuracy. A scenario: Calculating the coordinates of a forward station from a given base station would require [perhaps] the bearing (an angle from north, say) and distance from hither to there. Calculating the north coordinate would set up this relationship, e.g.: cos(3° 22' 49.6") x 415.9207'(Hyp) = adjacent side(North) My first requirement, and this is the struggle I (we) are now engaged in, is to convert my bearing angle (3° 22' 49.6") to decimal degrees, such that I can assign its' proper cosine value. Now, I am multiplying these two very refined values (yes, the distance really is honed down to 10,000'ths of a foot-that's normal in surveying data); within the bowels of the computer's blackboard scratch-pad, I cannot allow errors to evolve and emerge. Were I to accumulate many of these "legs" into perhaps a 15 mile traverse-accumulating little computer errors along the way-the end result could be catastrophically wrong. (Recall that in the great India Survey in the 1800's, Waugh got the elevation of Mt. Everest wrong by almost 30' feet for just this exact same reason.) In surveying, we have a saying, "Measure with a micrometer, mark with chalk, cut with an axe". Accuracy [in math] is a sacred tenet. So, I am setting my self very high standards of accuracy, simply because those are the standards imposed by the project I am adapting, and I can require nothing less of my finished project. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
Lowell Tackett wrote: --- On Tue, 4/20/10, Steven D'Aprano wrote: From: Steven D'Aprano The simplest, roughest way to fix these sorts of problems (at the risk of creating *other* problems!) is to hit them with a hammer: round(18.15*100) == 1815 True Interestingly, this is the [above] result when I tried entered the same snippet: Python 2.5.1 (r251:54863, Oct 14 2007, 12:51:35) [GCC 3.4.1 (Mandrakelinux 10.1 3.4.1-4mdk)] on linux2 Type "help", "copyright", "credits" or "license" for more information. round(18.15)*100 == 1815 False But you typed it differently than Steven. He had round(18.15*100), and you used round(18.15)*100 Very different. His point boils down to comparing integers, and when you have dubious values, round them to an integer before comparing. I have my doubts, since in this case it would lead to bigger sloppiness than necessary. round(18.154 *100) == 1815 probably isn't what you'd want. So let me ask again, are all angles a whole number of seconds? Or can you make some assumption about how accurate they need to be when first input (like tenths of a second, or whatever)? If so use an integer as follows: val = rounddegrees*60)+minutes)*60) + seconds)*10) The 10 above is assuming that tenths of a second are your quantization. HTH DaveA ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
On Tue, 20 Apr 2010 19:11:24 -0700 (PDT) Lowell Tackett wrote: > > >>> round(18.15*100) == 1815 > > True > > Interestingly, this is the [above] result when I tried entered the same > snippet: > > Python 2.5.1 (r251:54863, Oct 14 2007, 12:51:35) > [GCC 3.4.1 (Mandrakelinux 10.1 3.4.1-4mdk)] on linux2 > Type "help", "copyright", "credits" or "license" for more information. > >>> round(18.15)*100 == 1815 > False > >>> > > But...I'm just offering that for its' curiosity value, not to contradict your > comments or the case you are making. hum hum hum... Denis vit esse estrany ☣ spir.wikidot.com ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
>From the virtual desk of Lowell Tackett --- On Tue, 4/20/10, Steven D'Aprano wrote: > From: Steven D'Aprano > Subject: Re: [Tutor] the binary math "wall" > To: tutor@python.org > Date: Tuesday, April 20, 2010, 7:39 PM > On Wed, 21 Apr 2010 02:58:06 am > Lowell Tackett wrote: > > I'm running headlong into the dilemma of binary math > representation, > with game-ending consequences, e.g.: > > >>> 0.15 > > > > 0.14999 > > > > Obviously, any attempts to manipulate this value, > under the misguided > > assumption that it is truly "0.15" are ill-advised, > with inevitable > > bad results. > > That really depends on what sort of manipulation you are > doing. > > >>> x = 0.15 > >>> x > 0.14999 > >>> x*100 == 15 > True > > Seems pretty accurate to me. > > However: > > >>> 18.15*100 == 1815 > False > > > The simplest, roughest way to fix these sorts of problems > (at the risk > of creating *other* problems!) is to hit them with a > hammer: > > >>> round(18.15*100) == 1815 > True Interestingly, this is the [above] result when I tried entered the same snippet: Python 2.5.1 (r251:54863, Oct 14 2007, 12:51:35) [GCC 3.4.1 (Mandrakelinux 10.1 3.4.1-4mdk)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> round(18.15)*100 == 1815 False >>> But...I'm just offering that for its' curiosity value, not to contradict your comments or the case you are making. > > [...] > > What I'm shooting...is an algorithm > that converts a > > deg/min/sec formatted number to decimal degrees. > It [mostly] worked...which exposed the flaw. > > I'm afraid that due to the nature of floating point, this > is a hard > problem. Even the professionals at Hewlett-Packard's > scientific > calculator division don't always get it right, and they are > *extremely* > careful: > > http://www.hpmuseum.org/cgi-sys/cgiwrap/hpmuseum/archv018.cgi?read=132690 > Interesting that you raise the *hallowed* 48GX as a standard. I have one (of the two I own) sitting next to me here, and have been using it as the bar against which to compare my computer. Using the HMS+/-/-> etc. functions, I get pretty darned accurate results. (Wish I'd known in time that HP was gonna throw the "48's" down the drain-I would own a lot more than two of them!) > The best result I can suggest is, change the problem! Don't > pass > degrees-minutes-seconds around using a floating point > value, but as a > tuple with distinct (DEG, MIN, SEC) integer values. Or > create a custom > class. > > But if you really need D.MMSS floats, then something like > this should be > a good start.: > > def dms2deg(f): > """Convert a floating point number formatted > as D.MMSS > into degrees. > """ > mmss, d = math.modf(f) > assert d == int(f) > if mmss >= 0.60: > raise ValueError( > 'bad fractional part, expected > < .60 but got %f' % mmss) > mmss *= 100 > m = round(mmss) > if m >= 60: > raise ValueError('bad minutes, > expected < 60 but got %d' % m) > s = round((mmss - m)*100, 8) > if not 0 <= s < 60.0: > raise ValueError('bad seconds, > expected < 60.0 but got %f' % s) > return d + m/60.0 + s/3600.0 > > > >>> dms2deg(18.15) > 18.25 > >>> dms2deg(18.1515) > 18.2541666 > > > which compares well to my HP-48GX: > > 18.15 HMS-> > > gives 18.25, and: > > 18.1515 HMS-> > > gives 18.254167. > > > Note though that this still fails with some valid input. I > will leave > fixing it as an exercise (or I might work on it later, time > > permitting). > > > -- > Steven D'Aprano > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > http://mail.python.org/mailman/listinfo/tutor > What you've provided with your comments is more of what I've received wholesale in this entire discourse--an incredible wealth of new insight and ways of looking at the problem. Don't think you grasp how new I am at this, and how even what little I've tried to "pull off"-on my own-is way out at the edge of the "box" for me. Someone wondered if performance was an issue that could effect my choice
Re: [Tutor] the binary math "wall"
On Wed, 21 Apr 2010 02:58:06 am Lowell Tackett wrote: > I'm running headlong into the dilemma of binary math representation, with game-ending consequences, e.g.: > >>> 0.15 > > 0.14999 > > Obviously, any attempts to manipulate this value, under the misguided > assumption that it is truly "0.15" are ill-advised, with inevitable > bad results. That really depends on what sort of manipulation you are doing. >>> x = 0.15 >>> x 0.14999 >>> x*100 == 15 True Seems pretty accurate to me. However: >>> 18.15*100 == 1815 False The simplest, roughest way to fix these sorts of problems (at the risk of creating *other* problems!) is to hit them with a hammer: >>> round(18.15*100) == 1815 True [...] > What I'm shooting for, by the way, is an algorithm that converts a > deg/min/sec formatted number to decimal degrees. It [mostly] worked, > until I stumbled upon the peculiar cases of 15 minutes and/or 45 > minutes, which exposed the flaw. I'm afraid that due to the nature of floating point, this is a hard problem. Even the professionals at Hewlett-Packard's scientific calculator division don't always get it right, and they are *extremely* careful: http://www.hpmuseum.org/cgi-sys/cgiwrap/hpmuseum/archv018.cgi?read=132690 The best result I can suggest is, change the problem! Don't pass degrees-minutes-seconds around using a floating point value, but as a tuple with distinct (DEG, MIN, SEC) integer values. Or create a custom class. But if you really need D.MMSS floats, then something like this should be a good start.: def dms2deg(f): """Convert a floating point number formatted as D.MMSS into degrees. """ mmss, d = math.modf(f) assert d == int(f) if mmss >= 0.60: raise ValueError( 'bad fractional part, expected < .60 but got %f' % mmss) mmss *= 100 m = round(mmss) if m >= 60: raise ValueError('bad minutes, expected < 60 but got %d' % m) s = round((mmss - m)*100, 8) if not 0 <= s < 60.0: raise ValueError('bad seconds, expected < 60.0 but got %f' % s) return d + m/60.0 + s/3600.0 >>> dms2deg(18.15) 18.25 >>> dms2deg(18.1515) 18.2541666 which compares well to my HP-48GX: 18.15 HMS-> gives 18.25, and: 18.1515 HMS-> gives 18.254167. Note though that this still fails with some valid input. I will leave fixing it as an exercise (or I might work on it later, time permitting). -- Steven D'Aprano ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
On Tue, 20 Apr 2010 14:45:50 -0400 Dave Angel wrote: > If all values are made up of degrees/minutes/seconds, and seconds is a > whole number, then store values as num-seconds, and do all arithmetic on > those values. Only convert them back to deg/min/sec upon output. This seems the most direct answer of the issue. If performance is not critical, I would even write a small Angle type with ° ' '' int attributes to get rid of (read: abstract away) all complexity once and for all. Denis vit esse estrany ☣ spir.wikidot.com ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
Lowell Tackett wrote: I'm running headlong into the dilemma of binary math representation, with game-ending consequences, e.g.: 0.15 0.14999 Obviously, any attempts to manipulate this value, under the misguided assumption that it is truly "0.15" are ill-advised, with inevitable bad results. the particular problem I'm attempting to corral is thus: math.modf(18.15) (0.14858, 18.0) with some intermediate scrunching, the above snippet morphs to: (math.modf(math.modf(18.15)[0]*100)[0])/.6 1.4298 The last line should be zero, and needs to be for me to continue this algorithm. Any of Python's help-aids that I apply to sort things out, such as formatting (%), or modules like "decimal" do nothing more than "powder up" the display for visual consumption (turning it into a string). The underlying float value remains "corrupted", and any attempt to continue with the math adapts and re-incorporates the corruption. What I'm shooting for, by the way, is an algorithm that converts a deg/min/sec formatted number to decimal degrees. It [mostly] worked, until I stumbled upon the peculiar cases of 15 minutes and/or 45 minutes, which exposed the flaw. What to do? I dunno. I'm throwing up my hands, and appealing to the "Council". (As an [unconnected] aside, I have submitted this query as best I know how, using plain text and the "tu...@..." address. There is something that either I, or my yahoo.com mailer *or both* doesn't quite "get" about these mailings. But, I simply do my best, following advice I've been offered via this forum. Hope this --mostly-- works.) >From the virtual desk of Lowell Tackett One of the cases you mention is 1.666The decimal package won't help that at all. What the decimal package does for you is two-fold: 1) it means that what displays is exactly what's there 2) it means that errors happen in the same places where someone doing it "by hand" will encounter. But if you literally have to support arbitrary rational values (denominators other than 2 or 5), you would need to do fractions, either by explicitly keeping sets of ints, or by using a fractions library. And if you have to support arbitrary arithmetic, there's no answer other than hard analysis. This is not a Python-specific problem. Floating point has had such issues in every language I've dealt with since 1967, when I first learned Fortran. If you compare two values, the simplest mechanism is abs(a-b) < delta where you have to be clever about what small value to use for delta. If all values are made up of degrees/minutes/seconds, and seconds is a whole number, then store values as num-seconds, and do all arithmetic on those values. Only convert them back to deg/min/sec upon output. DaveA ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
>From the virtual desk of Lowell Tackett --- On Tue, 4/20/10, Luke Paireepinart wrote: > From: Luke Paireepinart > Subject: Re: [Tutor] the binary math "wall" > To: "Lowell Tackett" > Cc: "tutor" > Date: Tuesday, April 20, 2010, 1:20 PM > On Tue, Apr 20, 2010 at 11:58 AM, > Lowell Tackett > > wrote: > > I'm running headlong into the dilemma of binary math > representation, with game-ending consequences, e.g.: > > > >>>> 0.15 > > 0.14999 > > > > Yes, floats are slightly inaccurate. > > But I think your problem is that your math is wrong. > You are assuming... > If your equations cannot handle this, then coerce the value > to 1. > if .999 < i < 1.1: > i = 1 > > And before you say "but that is just a hack", no, that is > the nature... > But you really just need to adapt... > > May I suggest another approach though? > > Consider this: > >>> a = 18.15 > >>> a > 18.149 > >>> a = '18.15' > >>> degree, min = map(int, a.split('.')) > >>> degree > 18 > >>> min > 15 > > Supposing you get your input as a string. > Basically the second you let your value end up stored as a > float, > there's no turning back... > > Eventually you will develop a distrust for floats... > > Hope that helps, > -Luke > I was gonna go out jogging - and in a coupla hours check to see if anyone had tried to take this "on"; going out the door I decided to take a quick look. Ahem... These responses just "blow me away" on a couple of levels. I'm choosing "Luke"'s offering as representative. Within some great folks had read, parsed, and re-structured my query. My narrow focus yells out my inexperience...I was trying as hard as I could, with limited tools, to solve the issue. That's fine. But...the breadth and depth of the responses represent nothing short of a paradigm shift in my window into this world of "hacking". Ya'll have insights of a scale unimagined in the confines of my "thinking box". (Now, I gotta go out and jog just to clear my head!) Then come back...and pull the blinds closed...and absorb; and come up for air in maybe a week or so. And only then, be able to offer my thanks with an objective sense of how much I've been helped today...Wow! ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
On 04/21/10 02:58, Lowell Tackett wrote: > I'm running headlong into the dilemma of binary math representation, with > game-ending consequences, e.g.: > Never use float for representing numbers, use float to represent a "magnitude", do not rely on the exact representation of the number itself. If you need to control the representation of your number, either keep the number as integers or string or use fixed-point arithmetic (Decimal), depending on your use case. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
On Tue, 20 Apr 2010 09:58:06 -0700 (PDT) Lowell Tackett wrote: > I'm running headlong into the dilemma of binary math representation, with > game-ending consequences, e.g.: > > >>> 0.15 > 0.14999 > [...] > The last line should be zero, and needs to be for me to continue this > algorithm. > > Any of Python's help-aids that I apply to sort things out, such as formatting > (%), or modules like "decimal" do nothing more than "powder up" the display > for visual consumption (turning it into a string). The underlying float > value remains "corrupted", You are wrong: >>> from decimal import Decimal >>> s = "0.15" >>> Decimal(s) == float(s) This shows, maybe weirdly, that the decimal version != 0.14999 --since it is actually equal to 0.15. Decimals are *not* internally represented as binary values, but instead with groups of bits each coding a *decimal* digit; this is precisely the point. Else why do you think developpers would cope with such a problem? Denis vit esse estrany ☣ spir.wikidot.com ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
On Tue, Apr 20, 2010 at 11:58 AM, Lowell Tackett wrote: > I'm running headlong into the dilemma of binary math representation, with > game-ending consequences, e.g.: > > >>> 0.15 > 0.14999 > > Obviously, any attempts to manipulate this value, under the misguided > assumption that it is truly "0.15" are ill-advised, with inevitable bad > results. > > the particular problem I'm attempting to corral is thus: > > >>> math.modf(18.15) > (0.14858, 18.0) > > with some intermediate scrunching, the above snippet morphs to: > > >>> (math.modf(math.modf(18.15)[0]*100)[0])/.6 > 1.4298 > > The last line should be zero, and needs to be for me to continue this > algorithm. > > Any of Python's help-aids that I apply to sort things out, such as > formatting (%), or modules like "decimal" do nothing more than "powder up" > the display for visual consumption (turning it into a string). The > underlying float value remains "corrupted", and any attempt to continue with > the math adapts and re-incorporates the corruption. > That is not precisely correct - modf first converts decimal to a float and then applies the calculation. Try this instead: def modf(mydecimal): num = decimal.Decimal('1.0') return (mydecimal%num, mydecimal//num) On my machine this returns (with In [18]: modf(d) Out[18]: (Decimal('0.15'), Decimal('18')) Which are easily converted. In [30]: (modf(modf(d)[0]*100)[0])/decimal.Decimal('.6') Out[30]: Decimal('0.0') So your problem with decimal isn't that it lacks the precision you're seeking - you're simply converting it to a float /before/ performing the calculations, which makes turning it into a decimal in the first place pretty much useless. HTH, Wayne ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
On Tue, Apr 20, 2010 at 12:58 PM, Lowell Tackett wrote: > Any of Python's help-aids that I apply to sort things out, such as formatting > (%), or modules like "decimal" do nothing more than "powder up" the display > for visual consumption (turning it into a string). The underlying float > value remains "corrupted", and any attempt to continue with the math adapts > and re-incorporates the corruption. Using the decimal module does not convert anything to strings. The decimal module gives you floating point arithmetic with base 10 rather than base 2. You examples seem to work okay for me using decimals: IDLE 2.6.4 >>> import decimal >>> n = decimal.Decimal("18.15") >>> n Decimal('18.15') >>> print n 18.15 >>> divmod(n, 1) (Decimal('18'), Decimal('0.15')) >>> divmod(divmod(n, 1)[1]*100, 1)[1]/decimal.Decimal("0.6") Decimal('0.0') If you need to avoid floating point calculations entirely, you might try the fractions module (new in python 2.6): >>> import fractions >>> n = fractions.Fraction("18.15") >>> n Fraction(363, 20) >>> print n 363/20 >>> divmod(divmod(n, 1)[1]*100, 1)[1]/fractions.Fraction("0.6") Fraction(0, 1) >>> print divmod(divmod(n, 1)[1]*100, 1)[1]/fractions.Fraction("0.6") 0 and if you need to convert to float at the end: >>> float(divmod(divmod(n, 1)[1]*100, 1)[1]/fractions.Fraction("0.6")) 0.0 >>> -- Jerry ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] the binary math "wall"
On Tue, Apr 20, 2010 at 11:58 AM, Lowell Tackett wrote: > I'm running headlong into the dilemma of binary math representation, with > game-ending consequences, e.g.: > 0.15 > 0.14999 > > Obviously, any attempts to manipulate this value, under the misguided > assumption that it is truly "0.15" are ill-advised, with inevitable bad > results. > Yes, floats are slightly inaccurate. No, this usually doesn't cause problems. You can use the decimal module if you want. But I think your problem is that your math is wrong. You are assuming that your float values are precise and they are not. You must check ranges when dealing with float values, not for specific values. I.E. you should never depend on a value being 1 in a float calculation, instead your equations need to be robust enough to deal with values very close to 1 as well. If your equations cannot handle this, then coerce the value to 1. if .999 < i < 1.1: i = 1 And before you say "but that is just a hack", no, that is the nature of floating-point values. No one ever claimed that they were precise or that you should depend on their values being precise. If you really care so much, use the decimal module. But you really just need to adapt your formulas from the ideal to the reality, in which the values are not necessarily complete. May I suggest another approach though? Why even process these values as floats? Consider this: >>> a = 18.15 >>> a 18.149 >>> a = '18.15' >>> degree, min = map(int, a.split('.')) >>> degree 18 >>> min 15 Supposing you get your input as a string. Basically the second you let your value end up stored as a float, there's no turning back. You need to get ahold of this value before it becomes a float and deal with it in a different way. The most straightforward is to deal with the parts individually as integers. Eventually you will develop a distrust for floats and you will intrinsically know where and when you should / shouldn't use them ;) Hope that helps, -Luke ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
[Tutor] the binary math "wall"
I'm running headlong into the dilemma of binary math representation, with game-ending consequences, e.g.: >>> 0.15 0.14999 Obviously, any attempts to manipulate this value, under the misguided assumption that it is truly "0.15" are ill-advised, with inevitable bad results. the particular problem I'm attempting to corral is thus: >>> math.modf(18.15) (0.14858, 18.0) with some intermediate scrunching, the above snippet morphs to: >>> (math.modf(math.modf(18.15)[0]*100)[0])/.6 1.4298 The last line should be zero, and needs to be for me to continue this algorithm. Any of Python's help-aids that I apply to sort things out, such as formatting (%), or modules like "decimal" do nothing more than "powder up" the display for visual consumption (turning it into a string). The underlying float value remains "corrupted", and any attempt to continue with the math adapts and re-incorporates the corruption. What I'm shooting for, by the way, is an algorithm that converts a deg/min/sec formatted number to decimal degrees. It [mostly] worked, until I stumbled upon the peculiar cases of 15 minutes and/or 45 minutes, which exposed the flaw. What to do? I dunno. I'm throwing up my hands, and appealing to the "Council". (As an [unconnected] aside, I have submitted this query as best I know how, using plain text and the "tu...@..." address. There is something that either I, or my yahoo.com mailer *or both* doesn't quite "get" about these mailings. But, I simply do my best, following advice I've been offered via this forum. Hope this --mostly-- works.) >From the virtual desk of Lowell Tackett ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor