# Test-driven Python learning

Bugs are nice because I can learn from them. Recently I found a bug that taught me about floats in Python and how not to use them. I found it thanks to an automated test.

### The test

In a legal contract I had to print a monetary amount in a format similar to

`MXN $11,600.00 (eleven thousand, six hundred pesos 00/100 cents)`

My tests check the LaTeX source of the resulting PDF, looking for the correctly formatted string `11,600.00`

, which represents an amount of `11,000`

something plus a VAT of 16%, and also for the string `eleven thousand, six hundred`

.

It essentially goes like this:

from app.models import LeaseAgreement def test_contract_states_rent_plus_vat(): """Test correct statement of rent amount with and without IVA (VAT (USt (MWSt))).""" contract = LeaseAgreement.build( rent_amount=Decimal(10000.00) ) tex = contract.render_tex() assert '10,000.00' in tex assert '11,600.00' in tex assert 'eleven thousand, six hundred' in tex

The first two asserts did not fail, the third one did. My code actually printed

`MXN $11,600.00 (eleven thousand, five hundred and ninety-nine pesos 00/100 cents)`

### The bug

Turns out I had misused Python's `Decimal`

class to calculate the amount plus VAT. Like this:

>>> x = Decimal(10000) * Decimal(1.16)

Printing the number in the desired format works fine.

>>> print('{:,.2f}'.format(x)) 11,600.00

But rounding down for only printing the amount without decimal places does not.

>>> num2words(int(x)) 'eleven thousand, five hundred and ninety-nine'

That is because floats are not exact.

>>> Decimal(1.16) Decimal('1.1599999999999999200639422269887290894985198974609375') >>> int(Decimal(1.16)*10000) 11599

### The fix

Working with monetary amounts that round to 2 digits requires care. Thanks to Rami and others, I now know that I should have used e.g.

Decimal('1.16')

and

Decimal('1.16').quantize(Decimal('.01'), rounding=ROUND_HALF_UP)

when apropriate.