Blue Week Special Offer | Brighten your week!
days
hours
minutes
seconds

Potential bug in exercice: Dividing Apples

My answer to the quest is correct. The “answer output” said that the answer is different that the one was pointed as expected true answer.

Dividing Apples

# provided input
num_apples = 100
num_people = 8
# answer to this input: 12, 4

def divide_apples(num_apples, num_people):
    
    apples_each = num_apples // num_people
    apples_left_hole_number = num_apples / num_people
    apples_left = int((apples_left_hole_number - apples_each) * num_people)
    return apples_each, apples_left

    
    
print(divide_apples(num_apples, num_people))

dataquest answer:

2 Likes

Hi @drill_n_bass,

There’s no bug here. It’s due to the imperfection of Python when it deals with float numbers. In this article you can read about the limitations of Python when doing calculations with float numbers. In short, it tries to render decimal floats into binary ones, and this sometimes can lead to weird results (the decimal float being rounded by Python non precisely). Let’s say, it’s a bug of Python itself :slightly_smiling_face:

Exactly in your code this issue happens in this line:

apples_left = int((apples_left_hole_number - apples_each) * num_people)

If we have num_apples=37 and num_people=7, as suggested in the error, we would have here:

apples_left = int((5.285714285714286 - 5) * 7)

that is

apples_left = int(0.285714285714286 * 7)

which mathematically should be equal to int(2.0000000000000018) = 2.
In reality, due to the rounding issues of Python in case of 0.285714285714286, we have something different:

int(1.9999999999999991) =  1

That’s why you get this error here.

Try this code instead:

def divide_apples(num_apples, num_people):   
    return num_apples // num_people, num_apples % num_people
3 Likes

Can you please explain this part again, I didn’t get it.

1 Like

Hi @raisa.jerin.sristy79,

This 0.285714285714286 is a periodical fraction, with the succession of numbers “285714” being repeated infinitively. Even without considering this bug in Python

(i.e, the fact that it tries to translate decimal fractions into binary in its memory, but most decimal fractions cannot be represented exactly as binary fractions, and so are only approximated by the binary floating-point numbers actually stored in the machine),

if you take, say, 0.285714285714285714 and start rounding it every time by the last decimal and then multiply it by 7 (by hand, not in Jupyter or even Excel, which also rounds it automatically), you’ll see that every time the whole part changes from 1 to 2, which actually makes difference. Let’s try to do it in Jupyter, despite its rounding issues, and see what will happen:

print(0.285714285714285714*7)
print(0.28571428571428571*7)
print(0.2857142857142857*7)
print(0.285714285714286*7)
print(0.28571428571429*7)
print(0.2857142857143*7)
print(0.285714285714*7)
print(0.28571428571*7)
print(0.2857142857*7)
print(0.285714286*7)
print(0.28571429*7)
print(0.2857143*7)
print(0.285714*7)
print(0.28571*7)
print(0.2857*7)
print(0.286*7)
print(0.29*7)
print(0.3*7)

2.0
2.0
2.0
2.0000000000000018
2.0000000000000298
2.0000000000001004
1.9999999999979998
1.99999999997
1.9999999999
2.0000000019999997
2.00000003
2.0000001
1.9999980000000002
1.9999700000000002
1.9999
2.002
2.03
2.1

We see that, first, the whole part really switchs from 1 to 2 for different roundings, second, actually it even doesn’t round this fraction correctly mathematically because of its binary translation issues (actually, I was not precise in my previous reply: it was not mathematically here, but “Pythonically” :slightly_smiling_face:).

Another weird thing here, returning to our mission task, is:

int((5.285714285714286-5)*7)
1

but:

int(0.285714285714286*7)
2

But we should have the same decimal number in the inner brackets! Instead, we have:

print(0.285714285714286*7)
print((5.285714285714286-5)*7)

2.0000000000000018
1.9999999999999991

which is not even strictly mathematically indeed, but is a Python’s way to digest long decimal fractions. In our case, the fraction itself is periodical and has per se the issue of changing the whole part from 1 to 2, depending on where we decide to round it.

3 Likes

@Elena_Kosourova @raisa.jerin.sristy79

Great details! That is why should not ‘abuse’ int() for rounding! Depending on the use case:

  • Round your float via calling round() without supplying any argument for the number of decimal places (also not 0). This converts the input float to an int under the hood.
# Examples
print(round(0.285714285714286*7))
print(round((5.285714285714286-5)*7))

# Results
2
2
  • Use floor division (//) as suggested above by @Elena_Kosourova for this particular problem. But make sure that both inputs (divider and divisor) are int's. Otherwise you have to deal with type conversion again.

Best
htw

3 Likes