## Decimals

We’ve seen that floats in Python can sometimes be inaccurate due to roundoff error.  One way to correct this is to use Python’s Decimal data type. A Decimal variable allows the number of decimal points to be fixed, which can often eliminate roundoff error.

Decimals have a somewhat peculiar and clumsy syntax, however, so it’s worth looking at a few simple examples. The `Decimal` data type (with an uppercase ‘D’) is included in the `decimal` (with a lowercase ‘d’) module, so it must be imported before it can be used. [If you want to see the full contents of the decimal module, you can type `help(decimal)` in the console. However, be prepared for a very long list resulting from this. It’s probably more instructive to refer to the official documentation page on decimals.]

Intuitively, you might expect that simply feeding an ordinary float number into Decimal would convert it to a Decimal. To test this, try this in the console:

```from decimal import *
Decimal(0.1)```

The * in the first line imports everything from the decimal module.

Decimal(‘0.1000000000000000055511151231257827021181583404541015625’)

The result, despite the huge number of significant digits, is certainly not exactly 0.1. However, if we enclose the 0.1 in quotes, we get:

```from decimal import *
Decimal('0.1')```

Now we get `Decimal('0.1')` as the result. Enclosing a number in quotes converts it to a string, so we see that in order to create a Decimal from a float, we must enclose the float in quotes to produce a string.

When we first import the decimal module, we can see its default context by calling `getcontext()`. The result is

Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])

The ‘prec’ is the precision, or number of decimal places, which defaults to 28.

An obvious application of Decimals is in keeping track of quantities of money, which are usually in decimal form, with 100 of some smaller unit, such as pence or cents, equal to a larger unit, such as a British pound or dollar. You might think that setting the ‘prec’ to 2 would allow precise calculations with money, but in fact, it doesn’t.

The problem is that ‘prec’ specifies the number of significant digits to retain, and not the number of digits following the decimal point. Thus, if we try

```from decimal import *
getcontext().prec = 2
sum = Decimal(2.5) + Decimal(10.47)
sum```

we get the result `Decimal('13')` and not 12.97, as we might hope. We can solve this in two ways. First, we can enclose the quantities 2.5 and 10.47 in quotes to convert them to strings, and reset the prec to some large number. That is, we have

```from decimal import *
getcontext().prec = 28
sum = Decimal('2.5') + Decimal('10.47')
sum```

This gives the desired result of `Decimal('12.97')`.

Second, if we don’t want to write the arguments to Decimal as strings, we can use the quantize() function, as follows:

```from decimal import *
sum = Decimal(2.5) + Decimal(10.47)
pence = Decimal('0.01')
sum.quantize(pence)
```

This also gives the correct result of `Decimal('12.97')`.

Despite their peculiarities, Decimals can be combined with other numeric types, although some caution is needed. You can perform the usual arithmetic operations combining Decimals and integers. For example, try the following (we’re assuming the prec is 28, the default):

```from decimal import *
sum = Decimal('2.5') + Decimal('10.47')
sum * 12
sum ** 3
2 ** sum```
However, if you try combining a Decimal with a float, things go wrong. Try `sum + 12.834`, and you’ll get the error message “TypeError: unsupported operand type(s) for +: ‘decimal.Decimal’ and ‘float'”.  The error message means that the + operator is not defined when one of its operands is a Decimal and the other is a float.
To get around this, we can convert the Decimal to a float: `float(sum) + 12.834`.  As we’re now combining two floats, the answer is also a float: 25.804000000000002. As you can see, we’re back in the roundoff error problem again. If we want to avoid this, we can convert the float to a string and then to a Decimal, so we have` sum + Decimal('12.834')`, which now yields an exact Decimal result of `Decimal('25.804')`. If we try to convert the float to a Decimal without first enclosing it in quotes, we get roundoff error again. The statement `sum3 + Decimal(12.834)` gives us (assuming prec is set to its default value of 28): `Decimal('25.80399999999999963051777740').`