[Tutor] script comparing two values

Jeff Shannon jeff@ccvcorp.com
Mon Feb 24 23:41:04 2003


Mic Forster wrote:

>def constant(j):
>	result = []
>	i = 0.0
>	while i is not j:
>		result.append(k)
>		i, k = (k / (1 - k)((1 - k)^s) / (1 - (1 - k)^s), i
>+ 0.0001
>	return result
>			
>SyntaxError: invalid syntax
>

The syntax error is because you didn't balance your parentheses properly 
-- you've got one more ( than you have ).  However, there's a few other 
problems with this code:

1.  The ^ operator is *not* pow() -- it's a bitwise XOR.  You can use ** 
instead -- (1-k)**s rather than (1-k)^s.

2.  You need to explicitly use a multiplication operator -- your 
"(1-k)((1-k)**s)" should be "(1-k) * ((1-k)**s)" instead.

3.  You are appending k to result before you first assign k.  This will 
get you an UnboundLocalError once you fix your mismatched parens.

4.  You may not get the result you expect from 'i is not j', for two 
different reasons.  
     4a.  The first of those reasons is that the 'is' operator tests for 
*identity* -- you're asking whether i and j are the same object in 
memory, not whether they have the same value.  For some objects, this 
might work by accident (for instance, Python creates static internal 
objects for all integers under 100, IIRC, so the integer 5 will have the 
same identity no matter how it's refered to), but that's not a good 
thing to rely on.  For most data, this condition will never be true. 
 You should be using equality, not identity, to end your loop.  

    4b.  The other reason has to do with floating point imprecision. 
 Floats are stored internally in a binary format which is based on 
(possibly negative) powers of two.  This internal format cannot always 
be perfectly converted to a decimal number.  Python will convert 
floating point numbers to the nearest possible internal value, but there 
is a necessary loss of precision.  This means that, when you repeatedly 
add 0.0001 to i, you're not going to get what you expect.

 >>> i = 0.0
 >>> for n in range(10):
...     i += 0.0001
...     print repr(i)
...
0.0001
0.00020000000000000001
0.00030000000000000003
0.00040000000000000002
0.00050000000000000001
0.00060000000000000006
0.0007000000000000001
0.00080000000000000015
0.00090000000000000019
0.0010000000000000002
 >>>

The conversion from decimal to binary and back to decimal cannot be done 
perfectly, so imprecision creeps in.  This means that if you pass in, 
say, 0.0009 as your value for j, expecting this to run for five 
iterations, you may instead have an endless loop because i will never 
equal 0.0009 -- it'll be 0.00090000000000000019 instead, and j will 
actually be 0.00089999999999999998.  

 >>> j = 0.0009
 >>> i = 0.0
 >>> for i in range(20):
...     i += 0.0001
...     if i == j:  print "Match!", repr(i)
...    
 >>> # No match?
 >>> j
0.00089999999999999998
 >>>

Direct comparisons for equality of floats are always hazardous for this 
reason, and you should instead check if one value is within a certain 
small range of another.

 >>> for n in range(10):
...     i += 0.0001
...     if (j-delta) < i < (j+delta):
...         print "Match!", repr(i)
...     else:
...         print "      ", repr(i)
...
       0.0001
       0.00020000000000000001
       0.00030000000000000003
       0.00040000000000000002
       0.00050000000000000001
       0.00060000000000000006
       0.0007000000000000001
       0.00080000000000000015
Match! 0.00090000000000000019
       0.0010000000000000002
 >>>

Hope that this helps.

Jeff Shannon
Technician/Programmer
Credit International