Number Format function

  • Thread starter Edward Hartfield
  • Start date
E

Edward Hartfield

I'm am relatively new to Python but use it daily. Today, I went looking
for a function, like PHP's number_function, that will take a number and
return a string with number formatted with grouped thousands and the
decimal portion rounded to a given number of places. This is certainly
needed when you want to take a floating-point value from a database and
display it as currency, for instance. I could not find what I was
looking for so I wrote the following function. I hope either (a) I've
provided something useful or (b) someone can tell me what I *should*
have done! Thanks.

import math

def number_format(num, places=0):
"""Format a number with grouped thousands and given decimal places"""

#is_negative = (num < 0)
#if is_negative:
# num = -num

places = max(0,places)
tmp = "%.*f" % (places, num)
point = tmp.find(".")
integer = (point == -1) and tmp or tmp[:point]
decimal = (point != -1) and tmp[point:] or ""

count = commas = 0
formatted = []
for i in range(len(integer) - 1, 0, -1):
count += 1
formatted.append(integer)
if count % 3 == 0:
formatted.append(",")

integer = "".join(formatted[::-1])
return integer+decimal
 
W

wittempj

Your code has a little bug, I highly recommend to add a test to your
code, for an idea see below - I fixed your code as well.

#!/usr/bin/env python
import math

def number_format(num, places=0):
"""Format a number with grouped thousands and given decimal
places"""
#is_negative = (num < 0)
#if is_negative:
# num = -num

places = max(0,places)
tmp = "%.*f" % (places, num)
point = tmp.find(".")
integer = (point == -1) and tmp or tmp[:point]
decimal = (point != -1) and tmp[point:] or ""

count = commas = 0
formatted = []
for i in range(len(integer) - 1, 0, -1):
count += 1
formatted.append(integer)
if count % 3 == 0:
formatted.append(",")
formatted.append(integer[0]) # this misses in your part
integer = "".join(formatted[::-1])
return integer+decimal


#
# add something like this: it helps to prevent you break your code
#
import unittest

class test_number_format(unittest.TestCase):
def test(self):
self.assertEqual(number_format(1000000, 2), '1,000,000.00')
self.assertEqual(number_format(100000, 2), '100,000.00')
self.assertEqual(number_format(100, 2), '100.00')
self.assertEqual(number_format(1000000.33, 2), '1,000,000.33')
self.assertEqual(number_format(1000000.333, 2), '1,000,000.33')
self.assertEqual(number_format(1000000.3, 2), '1,000,000.30')
self.assertEqual(number_format(123456, 2), '123,456.00')
self.assertEqual(number_format(12345, 2), '12,345.00')
self.assertEqual(number_format(123, 2), '123.00')
self.assertEqual(number_format(123456.33, 2), '123,456.33')
self.assertEqual(number_format(12345.333, 2), '12,345.33')
self.assertEqual(number_format(123.3, 2), '123.30')

suite = unittest.makeSuite(test_number_format)
unittest.TextTestRunner(verbosity=2).run(suite)
 
E

Edward Hartfield

Thanks. I noticed the bugs later. But after talking with my boss, he
suggested something more elegant (again *untested*, yet):

import locale

def number_format(num, places=0)
"""Format a number according to locality and given places"""
locale.setlocale(locale.LC_ALL, locale.getdefaultlocale()[0])
return locale.format("%.*f", (places, num), 1)
 
W

wittempj

This is a little faster:

def number_format(num, places=0):
"""Format a number according to locality and given places"""
locale.setlocale(locale.LC_ALL, "")
return locale.format("%.*f", (places, num), True)

I tested this ok with my test
 
R

Rick Zantow

def number_format(num, places=0):
"""Format a number according to locality and given places"""
locale.setlocale(locale.LC_ALL, "")
return locale.format("%.*f", (places, num), True)

There are some edge cases in the format conversion that could present
some issues. For example:
2,312,753.45

I would expect the first to produce the same results as the second, but,
I suppose because of one of floating point's features, it doesn't work
that way.
 
S

Sion Arrowsmith

Rick Zantow said:
2,312,753.45

I would expect the first to produce the same results as the second, but,
I suppose because of one of floating point's features, it doesn't work
that way.
2312753.4450000101

So, yeah, the nature of floating points is going to make that
first one round "unexpectedly".
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,149
Latest member
Vinay Kumar Nevatia0
Top