From 7cdff674215830aaad9b08f1b8a692a889ab1a21 Mon Sep 17 00:00:00 2001 From: moneromooo Date: Sat, 31 Jan 2015 12:19:56 +0000 Subject: [PATCH] AmountToString should now display without floating point inaccuracies --- tipbot/utils.py | 63 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/tipbot/utils.py b/tipbot/utils.py index a6a4667..2aaaaad 100644 --- a/tipbot/utils.py +++ b/tipbot/utils.py @@ -14,6 +14,8 @@ import hashlib import json import httplib import time +import math +from decimal import * import tipbot.config as config import tipbot.coinspecs as coinspecs from tipbot.log import log_error, log_warn, log_info, log_log @@ -77,6 +79,62 @@ def IsValidAddress(address): return True return False +# Code taken from the Python documentation +def moneyfmt(value, places=2, curr='', sep=',', dp='.', + pos='', neg='-', trailneg=''): + """Convert Decimal to a money formatted string. + + places: required number of places after the decimal point + curr: optional currency symbol before the sign (may be blank) + sep: optional grouping separator (comma, period, space, or blank) + dp: decimal point indicator (comma or period) + only specify as blank when places is zero + pos: optional sign for positive numbers: '+', space or blank + neg: optional sign for negative numbers: '-', '(', space or blank + trailneg:optional trailing minus indicator: '-', ')', space or blank + + >>> d = Decimal('-1234567.8901') + >>> moneyfmt(d, curr='$') + '-$1,234,567.89' + >>> moneyfmt(d, places=0, sep='.', dp='', neg='', trailneg='-') + '1.234.568-' + >>> moneyfmt(d, curr='$', neg='(', trailneg=')') + '($1,234,567.89)' + >>> moneyfmt(Decimal(123456789), sep=' ') + '123 456 789.00' + >>> moneyfmt(Decimal('-0.02'), neg='<', trailneg='>') + '<0.02>' + + """ + q = Decimal(10) ** -places # 2 places --> '0.01' + sign, digits, exp = value.quantize(q).as_tuple() + result = [] + digits = map(str, digits) + build, next = result.append, digits.pop + if sign: + build(trailneg) + for i in range(places): + build(next() if digits else '0') + build(dp) + if not digits: + build('0') + i = 0 + while digits: + build(next()) + i += 1 + if i == 3 and digits: + i = 0 + build(sep) + build(curr) + build(neg if sign else pos) + s = ''.join(reversed(result)) + + if dp in s: + s=s.strip('0').rstrip(dp) + if s=="" or s[0]==dp: + s="0"+s + return s + def AmountToString(amount): if amount == None: amount = 0 @@ -85,12 +143,13 @@ def AmountToString(amount): if lamount == 0: samount = "0 %s" % coinspecs.name else: + places=long(0.5+math.log10(coinspecs.atomic_units)) for den in coinspecs.denominations: if lamount < den[0]: - samount = "%.16g %s" % (float(lamount) / den[1], den[2]) + samount = moneyfmt(Decimal(lamount)/Decimal(den[1]),places=places) + " " + den[2] break if not samount: - samount = "%.16g %s" % (float(lamount) / coinspecs.atomic_units, coinspecs.name) + samount = moneyfmt(Decimal(lamount)/Decimal(coinspecs.atomic_units),places=places) + " " + coinspecs.name return samount def TimeToString(seconds):