Strange behavior of int()

B

Brian

Hello,

Can someone tell me what I am doing wrong in this code.

If I create a file change.py with the following contents:

def intTest(M, c):
r = M
for k in c:
print 'int(r/k) = ', int(r/k), 'r =', r, 'k =', k, 'r/k
=', r/k
r = r - (k*int(r/k))

intTest(2.30, [0.25, 0.10, 0.05, 0.01])

and execute it, I get the output:

int(r/k) = 9 r = 2.3 k = 0.25 r/k = 9.2
int(r/k) = 0 r = 0.05 k = 0.1 r/k = 0.5
int(r/k) = 0 r = 0.05 k = 0.05 r/k = 1.0
int(r/k) = 4 r = 0.05 k = 0.01 r/k = 5.0

Why is int(r/k), where r = 0.5 and k = 0.5 = 0? Shouldn't it be 1?
And why is the last one = 4 and not 5?

If I execute a similar command from the command line, it works just
fine:1

I have tested this on Linux, python 2.3.2 and 2.4.1 and Windows python
2.4.2
all with the same results.

I think I am doing something wrong in this line:
r = r - (k*int(r/k))

Thanks for any help.

Brian
 
R

Rob E

Why is int(r/k), where r = 0.5 and k = 0.5 = 0? Shouldn't it be 1?
And why is the last one = 4 and not 5?

I dont' know why the differences in your exact case. However, please
realise that Regardless of the programming language good programming
practice is to never rely on the int of a floating point division
-- or even the int of a floating point constant to be exactly
the integer value you expect it to be. This is because both
constant representations and math operations have rounding
error. Instead do less precise conversions by rounding. For
example:

a=b/c
if (a >= 0.0):
d = int(a+0.5)
else:
d = int(a-0.5)

If you don't do this sort of thing your programs generally
are senstivie to the details of foating point rounding --
which is generally dependent on processor, compilier and
in pythons case probably the runtime system.

Rob
 
D

Dan Bishop

Brian said:
Hello,

Can someone tell me what I am doing wrong in this code.

If I create a file change.py with the following contents:

def intTest(M, c):
r = M
for k in c:
print 'int(r/k) = ', int(r/k), 'r =', r, 'k =', k, 'r/k
=', r/k
r = r - (k*int(r/k))

intTest(2.30, [0.25, 0.10, 0.05, 0.01])

and execute it, I get the output:

int(r/k) = 9 r = 2.3 k = 0.25 r/k = 9.2
int(r/k) = 0 r = 0.05 k = 0.1 r/k = 0.5
int(r/k) = 0 r = 0.05 k = 0.05 r/k = 1.0
int(r/k) = 4 r = 0.05 k = 0.01 r/k = 5.0

The important thing to remember is that, as far as your computer is
concerned, there are no such numbers as 2.30, 0.10, 0.05, or 0.01.
What's actually stored is the closest binary equivalents. So your
intTest call is equivalent to

intTest(5179139571476070*2**(-51), [0.25, 7205759403792794*2**(-56),
7205759403792794*2**(-57), 5764607523034235*2**(-59)])

The first time through the loop, r = 5179139571476070*2**(-51) and k =
0.25, so r/k = 5179139571476070*2**(-49), which equals
9.199999999999999289457264239899814128875732421875. This is as close
to the desired 9.2 as you can get. So far, so good.

Now, it happens that the value k*int(r/k) = 2.25 is computed exactly.
Subtracting this from r gives

r = 5179139571476070*2**(-51) - 2.25
= 5179139571476070*2**(-51) - 5066549580791808*2**(-51)
= (5179139571476070 - 5066549580791808) * 2**(-51)
= 112589990684262*2**(-51)
= 7205759403792768*2**(-57)

My last computation here is to normalize the result to 53 significant
bits. But note that the last 6 of those are zero, because the result
was shifted by 6 places.

00000011001100110011001100110011001100110011001100110 * 2**(-51)
/ /
/ /
/ /
/ /
/ /
/ /
11001100110011001100110011001100110011001100110000000 * 2**(-57)
^^^^^^
padding

The loss of 6 significant bits means that this approximation of 0.05 is
slightly different from the direct approximation of 0.05. The worst
part is, it's slightly *less*

7205759403792768*2**(-57) # result of the computation
7205759403792794*2**(-57) # 0.05 as stored in the computer

This causes r/k to be slightly *less* than one, which makes int(r/k)
zero and f's up the rest of your computations.

The way to fix this is to use numbers that can be stored exactly in
binary. The simplest way is to represent monetary amounts as integer
numbers of cents
int(r/k) = 9 r = 230 k = 25 r/k = 9.2
int(r/k) = 0 r = 5 k = 10 r/k = 0.5
int(r/k) = 1 r = 5 k = 5 r/k = 1.0
int(r/k) = 0 r = 0 k = 1 r/k = 0.0

You might also consider using the decimal.Decimal class. (Of course,
you'll still have roundoff problems if working with nondecimal amounts
like 1/3 or 1/7. In that case, use a Rational class.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,149
Latest member
Vinay Kumar Nevatia0
Top