> gerardo arnaez wrote:
>
>> On Mon, 28 Mar 2005 09:27:11 -0500, orbitz <orbitz at drorbitz.ath.cx>
>> wrote:
>>
>>> Floats are inherintly inprecise. So if thigns arn't working like you
>>> expect don't be surprised if 0.15, 0.12, and 0.1 are closer to the same
>>> number than you think.
>>
>>
>>
>> Are you telling me that I cant expect 2 digit preceision?
>
>
> Not without using round. Have *NO* faith in floating points. This is
> especially true when you are creating the decimals via division and the
> like.


What makes one nervous when looking at your tests with floating point numbers is that calcuations that might lead up to one of the numbers in your test might actually be a tiny bit higher or a tiny bit lower than you expect. *However*, I think the thing to keep in mind is that if you are only a billionth or less away from the boundary, it really doesn't matter which way you go in terms of dosage--you are at the border.

Here is an example of two calculations that you think should get you to 2.1. One is actually just shy of it and the other just a bit past:

###
>>> 3*.7
2.0999999999999996
>>> 7*.3
2.1000000000000001
###

So if you have the following test to determine if it is time to increase someone's dose of medication and the results are based on some measurement being greater than 2.1, then...

###
>>> def test(x):
 if x>2.1:
  print 'increase dose'
 else:
  print 'decrease dose'

>>>
###

if you test the above computed values, you will get different results:

###
>>> dose1, dose2 = 3*.7, 7*.3
>>> test(dose1)
decrease dose
>>> test(dose2)
decrease dose
###

OR NOT! Why do they both come out to be the same even though one number is bigger than 2.1 and the other not? Because 2.1 is not actually '2.1':

###
>>> 2.1
2.1000000000000001
###

The 7*.3 is actually "equal" to 2.1: they both are 2.1000000000000001.

On the one hand, we want the boundaries so we can say, "if you are past this boundary you take the next sized dose"...but if you are so close to the boundary that you can just as well say that you are right on it, does it really matter which way you go? Other than bothering the deterministic nature, are there other reasons to be concerned? As far as economics, the '2,1' case will have people staying at a lower dose longer (as would 2.2, 2.6 and 2.7 which all are a tiny bit bigger than their mathematical values, just like 2.1). Just the opposite will happen at the 2.3, 2.4, 2.8, and 2.9 since these values are represented as numbers smaller than their mathematical counterparts:

###
>>> 2.9
2.8999999999999999
###

If you have Python 2.4 you might want to check out the decimal type that is now part of the language. There is a description at

http://www.python.org/doc/2.4/whatsnew/node9.html

which starts with:

"Python has always supported floating-point (FP) numbers, based on the underlying C double type, as a data type. However, while most programming languages provide a floating-point type, many people (even programmers) are unaware that floating-point numbers don't represent certain decimal fractions accurately. The new Decimal type can represent these fractions accurately, up to a user-specified precision limit. "

And it gives the following example:

Once you have Decimal instances, you can perform the usual mathematical operations on them. One limitation: exponentiation requires an integer exponent:


>>> import decimal >>> a = decimal.Decimal('35.72') >>> b = decimal.Decimal('1.73') >>> a+b Decimal("37.45") >>> a-b Decimal("33.99") >>> a*b Decimal("61.7956") >>> a/b Decimal("20.64739884393063583815028902")

Another suggestion is to use the Round replacement that I wrote about in response to the "Float precision untrustworthy~~~" thread. With it, you don't have to worry about how large or small your numbers are when you Round them (unlike with round()). You just decide on how many "parts per x" you are interested in with your numbers. e.g. if your calculations are only precise to about 1 part per hundred then you would round them to the 2nd (or maybe 3rd) digit and compare them. With the separation of code and data as already suggested this would look something like this:

###
def Round(x, n=0, sigfigs=False):
        if x==0: return 0
        if not sigfigs:
                return round(x,n)
        else:
                #round to nth digit counting from first non-zero digit
                # e.g. if n=3 and x = 0.002356 the result is 0.00236
                return round(x,n-1-int(floor(log10(abs(x)))))
 ...
     for cutoff, value in cutoffs:
         if Round(x,3) < Round(cutoff,3):
             return value
..
###

IMHO, I don't think that decimal is the way to go with such calculations since calculations performed with limited precision numbers shouldn't be treated as though they had infinite precision. From basic error analysis of the calculated values above, one shouldn't probably keep more than 3 digits in a/b result of Decimal("20.64739884393063583815028902"). On the other hand, perhaps one can keep all the digits until the end and then make the precision decision--this gives the best of both worlds.

/c


_______________________________________________ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor

Reply via email to