Login

fast non-locale aware float format

Author:
percivall
Posted:
January 26, 2013
Language:
Python
Version:
1.4
Tags:
floatformat
Score:
0 (after 0 ratings)

Django's floatformat is a good way to format a number if you require a specific amount of decimals. It is, however, very slow. In testing each floatformat call took 200–250 us, which means it'll take a second to render a page that floatformats 4000 numbers.

Much of the time comes from using Decimals. I looked at using the cdecimal module, and while it improved the speed, each call still clocked in at between 80 and 100 us.

fast_floatformat is not locale aware, and doesn't look at Django settings for USE_THOUSAND_SEPARATOR, but it'll take between 1.2 and 3 us per call for ints, floats and strings, and about 12 us per call for Decimals, giving you up to 800000 floatformatted numbers per second.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def fast_floatformat(number, places=-1, use_thousand_separator=False):
    """simple_floatformat(number:object, places:int) -> str
    
    Like django.template.defaultfilters.floatformat but not locale aware
    and between 40 and 200 times faster
    """
    try:
        number = float(number)
    except (ValueError, TypeError):
        return ''
    
    # floatformat makes -0.0 == 0.0
    if number == 0:
        number = 0
    
    neg_places = False
    if places < 0:
        places = abs(places)
        neg_places = True
    
    if places == 0:
        # %.0f will truncate rather than round
        number = round(number, places)
    
    # .format is noticably slower than %-formatting, use it only if necessary
    if use_thousand_separator:
        format_str = "{:,.%sf}" % places
        formatted_number = format_str.format(number)
    else:
        format_str = "%%.%sf" % places
        formatted_number = format_str % number
    
    # -places means formatting to places, unless they're all 0 after places
    if neg_places:
        str_number = str(number)
        if not "." in str_number:
            return str_number
        if len(str_number) > len(formatted_number):
            return formatted_number
        int_part, _, _ = formatted_number.partition(".")
        if str_number.rstrip("0")[-1] == ".":
            return int_part
    
    return formatted_number


# TEST AND VALIDATION
from django.template.defaultfilters import floatformat, special_floats
from decimal import Decimal as Decimal


vals = [
    None,
    '',
    1,
    1.9,
    2.0,
    0.1385798798,
    0.2,
    -0.5,
    -0.0,
    -5.0038,
    18343.3582828389,
    Decimal("-0.0"),
    Decimal("5.000083387"),
    Decimal("0E-7"),
    Decimal("780000.388"),
    "-0.5",
    "3.80",
    "foo",
]
vals.extend(special_floats)


def test_floatformat():
    for val in vals:
        yield check_equal, val, floatformat(val), fast_floatformat(val)
        yield check_equal, val, floatformat(val, 7), fast_floatformat(val, 7)
        yield check_equal, val, floatformat(val, -7), fast_floatformat(val, -7)
        yield check_equal, val, floatformat(val, 0), fast_floatformat(val, 0)


def check_equal(orig, a, b):
    assert a == b, '(%s) %s not equal with %s' % (orig, a, b)

More like this

  1. Quantize decimal template filter by bl4th3rsk1t3 6 years, 4 months ago
  2. per-function cache decorator by nicois 7 years, 7 months ago
  3. RPN template math by durka 6 years, 10 months ago
  4. Currency filter by kljensen 7 years, 7 months ago
  5. Effective content caching for mass-load site using redirect feature by nnseva 4 years, 2 months ago

Comments

Please login first before commenting.