Search code examples
pythondecimalstring-formatting

Present a Decimal in the most readable format


So I'm trying to make a function that returns a stringified decimal in the most readable format. These are the requirements I have created for myself:

  1. If the absolute exponent of the number is greater than or equal to 9, it should be presented using scientific notation (eg 25000000000 -> 2.5E+9, 0.0000000036 -> 3.6E-9)
  2. Otherwise, the number should be presented as a standard notation decimal, with minimal trailing zeros (eg 103.400000 -> 103.4, 0.000005600 -> 0.0000056)

Currently, I'm using some code that I modified from this answer, but I can't really see a way to get it to match my requirements exactly.

Currently, my solution is as follows:

def stringifyDecimal(d: Decimal):
    # If d is within a reasonable range for printing as a normal number
    a = abs(math.log10(abs(d)))
    if a < 9:
        r = d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
    else:
        r = d.normalize()
    return str(r)

This works great for larger numbers:

  • stringifyDecimal(D('1E5')) -> '10000'
  • stringifyDecimal(D('1E9')) -> '1E+9'

But for very small fractions, it will always be returned in scientific notation:

  • stringifyDecimal(D('1E-7')) -> '1E-7'

This is because of the ...else d.normalize() part of the 5th line, which returns a Decimal that will stringify to scientific notation by default. Sadly, formatting options won't work for this as they require me to know the exact precision that I want to format to.

Is there a way that I can force the Decimal type to display a very small fraction as a decimal rather than in scientific notation?


Solution

  • Ok so the most effective method I've found is to use string formatting.

    d = Decimal('152')
    print(f"{d:f}") # Format as a standard decimal
    print(f"{d:e}") # Format in scientific notation
    

    from this, my solution is as follows:

    def strDecimal_Sci(d: Decimal) -> str:
        return f"{d.normalize():e}"
    
    def strDecimal_Norm(d: Decimal) -> str:
        return f"{d.normalize():f}"
    
    def stringifyDecimal(d: Decimal) -> str:
        if d == 0: return "0"
        a = abs(math.log10(abs(d)))
        if a < 9:
            return strDecimal_Norm(d)
        else:
            return strDecimal_Sci(d)