Tuesday, 14th January 2014

# Setting decimal places

It is relatively easy to output a string to a specific number of decimal places using string formatting:

> "%.3f" % 3.1415927 > '3.142'

If you want to round to a variable number of decimal places you can use string formatting twice:

def roundTo(n, dp): s = "%%.%df" % dp return s % n print roundTo(3.1415927, 3)

This first generates a string using dp to define the number of decimal places (e.g. if dp is 3, then the string is "%.3f"). Note that %% is the code for %. Then it uses this string to round the number n as before.

The more recent string format function works like this:

"{0:.3f}".format(n)

And that allows you to create roundTo function like this:

{0:.{1}f}".format(n, dp)

It's not an easy-to-read expression. The 1 is replaced by the second value (using 0-indexing), then the 0 is replaced by the first value using the ".3f" method.

### Removing unnecessary decimal places

When making my SVG optimiser, I wanted to reduce the size of SVG by reducing decimals (paths were often given to six decimal places, when one is sufficient for most purposes). When testing using the method above, I found that it could introduce unneeded decimal places since all integer would be given have ".0" appended, tripling their size.

My solution, and there maybe better ones (such as using the decimal module), was to use regex. I first defined a pattern to find trailing zeros, capturing the zeros in one group and any non-zero numbers after the decimal place in a second.

import regex as re re_zeros = re.compile('\.(\d*?)(0+)$')

Then given a string that has been formatting, it is searched for trailing zeros. The string is truncated by the number of trailing zeros, and if there aren't any other digits after the decimal place, it is truncated by a further place to remove the decimal place.

z = re_zeros.search(str_n) if z: length = (len(z.group(2)) + (len(z.group(1)) == 0)) str_n = str_n[:-length]

## Post new comment