113 lines
5.3 KiB
Python
113 lines
5.3 KiB
Python
import math
|
|
|
|
|
|
def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=False):
|
|
"""
|
|
Add suffix to value, transform value to match new suffix and round it.
|
|
|
|
Keyword arguments:
|
|
val -- value to process
|
|
prec -- precision of final number (number of significant positions to show)
|
|
lowest -- lowest order for suffixizing for numbers 0 < |num| < 1
|
|
highest -- highest order for suffixizing for numbers |num| > 1
|
|
currency -- if currency, billion suffix will be B instead of G
|
|
forceSign -- if True, positive numbers are signed too
|
|
"""
|
|
if val is None:
|
|
return ""
|
|
# Define suffix maps
|
|
posSuffixMap = {3: "k", 6: "M", 9: "B" if currency is True else "G"}
|
|
negSuffixMap = {-6: u'\u03bc', -3: "m"}
|
|
# Define tuple of the map keys
|
|
# As we're going to go from the biggest order of abs(key), sort
|
|
# them differently due to one set of values being negative
|
|
# and other positive
|
|
posOrders = tuple(sorted(posSuffixMap.iterkeys(), reverse=True))
|
|
negOrders = tuple(sorted(negSuffixMap.iterkeys(), reverse=False))
|
|
# Find the least abs(key)
|
|
posLowest = min(posOrders)
|
|
negHighest = max(negOrders)
|
|
# By default, mantissa takes just value and no suffix
|
|
mantissa, suffix = val, ""
|
|
# Positive suffixes
|
|
if abs(val) > 1 and highest >= posLowest:
|
|
# Start from highest possible suffix
|
|
for key in posOrders:
|
|
# Find first suitable suffix and check if it's not above highest order
|
|
if abs(val) >= 10**key and key <= highest:
|
|
mantissa, suffix = val / float(10 ** key), posSuffixMap[key]
|
|
# Do additional step to eliminate results like 999999 => 1000k
|
|
# If we're already using our greatest order, we can't do anything useful
|
|
if posOrders.index(key) == 0:
|
|
break
|
|
else:
|
|
# Get order greater than current
|
|
prevKey = posOrders[posOrders.index(key) - 1]
|
|
# Check if the key to which we potentially can change is greater
|
|
# than our highest boundary
|
|
if prevKey > highest:
|
|
# If it is, bail - we already have acceptable results
|
|
break
|
|
# Find multiplier to get from one order to another
|
|
orderDiff = 10**(prevKey - key)
|
|
# If rounded mantissa according to our specifications is greater than
|
|
# or equal to multiplier
|
|
if roundToPrec(mantissa, prec) >= orderDiff:
|
|
# Divide mantissa and use suffix of greater order
|
|
mantissa, suffix = mantissa / orderDiff, posSuffixMap[prevKey]
|
|
# Otherwise consider current results as acceptable
|
|
break
|
|
# Take numbers between 0 and 1, and matching/below highest possible negative suffix
|
|
elif abs(val) < 1 and val != 0 and lowest <= negHighest:
|
|
# Start from lowest possible suffix
|
|
for key in negOrders:
|
|
# Get next order
|
|
try:
|
|
nextKey = negOrders[negOrders.index(key) + 1]
|
|
except IndexError:
|
|
nextKey = 0
|
|
# Check if mantissa with next suffix is in range [1, 1000)
|
|
if abs(val) < 10**(nextKey) and key >= lowest:
|
|
mantissa, suffix = val / float(10**key), negSuffixMap[key]
|
|
# Do additional step to eliminate results like 0.9999 => 1000m
|
|
# Check if the key we're potentially switching to is greater than our
|
|
# upper boundary
|
|
if nextKey > highest:
|
|
# If it is, leave loop with results we already have
|
|
break
|
|
# Find the multiplier between current and next order
|
|
orderDiff = 10**(nextKey - key)
|
|
# If rounded mantissa according to our specifications is greater than
|
|
# or equal to multiplier
|
|
if roundToPrec(mantissa, prec) >= orderDiff:
|
|
# Divide mantissa and use suffix of greater order
|
|
# Use special handling of zero key as it's not on the map
|
|
mantissa, suffix = mantissa / orderDiff, posSuffixMap[nextKey] if nextKey != 0 else ""
|
|
# Otherwise consider current results as acceptable
|
|
break
|
|
# Round mantissa according to our prec variable
|
|
mantissa = roundToPrec(mantissa, prec)
|
|
sign = "+" if forceSign is True and mantissa > 0 else ""
|
|
# Round mantissa and add suffix
|
|
result = u"{0}{1}{2}".format(sign, mantissa, suffix)
|
|
return result
|
|
|
|
|
|
def roundToPrec(val, prec):
|
|
# We're not rounding integers anyway
|
|
# Also make sure that we do not ask to calculate logarithm of zero
|
|
if int(val) == val:
|
|
return int(val)
|
|
# Find round factor, taking into consideration that we want to keep at least prec
|
|
# positions for fractions with zero integer part (e.g. 0.0000354 for prec=3)
|
|
roundFactor = int(prec - math.ceil(math.log10(abs(val))))
|
|
# But we don't want to round integers
|
|
if roundFactor < 0:
|
|
roundFactor = 0
|
|
# Do actual rounding
|
|
val = round(val, roundFactor)
|
|
# Make sure numbers with .0 part designating float don't get through
|
|
if int(val) == val:
|
|
val = int(val)
|
|
return val
|