Search code examples
pythonfloating-pointprecisionnumber-formatting

Reduce decimals shown for large numbers but not for small


When formatting floats, is there any python method/formatting for reducing how many decimals that is shown as the number becomes larger?

It could for instance be a limit to how many digits of the number that is shown. Example below:

 100.145 ->  100
   2.392 ->    2.39
  34.827 ->   34.8
4599.298 -> 4599

Solution

  • Update:

    As chux points out in his comment, my original solution does not work for boundary cases in which rounding leads to a carry. Also, I didn't pay attention to the fact that log10(100) == 2 != 3. The second bug is easy to come by. To fix the first one, I came up with a recursion. Now it should work but isn't simple anymore.

    import math
    
    def flexible_format(num_in, total_digits):
        try:
            digits_before_decimal = math.floor(math.log10(abs(num_in))) + 1
        except ValueError:
            # if num == 0
            return '0'
        digits_after_decimal = max(0, total_digits - digits_before_decimal)
        # if rounding increases number of digits, we have to format once again
        # after that, an additional rounding doesn't increase the number of digits
        # so we can be sure not to land in an infinite recursion 
        num_out = round(num_in, digits_after_decimal)
        if math.floor(math.log10(abs(num_out))) >= digits_before_decimal:
            return flexible_format(num_out, total_digits)
        return f'{num_out:.{digits_after_decimal}f}'
    
    list_nums =  [-100.145,  2.392, -34.827 , 4599.298, 99.95, 100 ]
    for num in list_nums:
        print(flexible_format(num, total_digits=3))
     
    # -100
    # 2.39
    # -34.8
    # 4599
    # 100
    # 100
    

    Original false solution:

    I don't know a commonly used function achieving that, but it is easy to implement.

    import math
    
    def flexible_format(num, total_digits):
        try:
            digits_before_decimal = math.ceil(math.log10(abs(num)))
        except ValueError:
            # if num == 0
            return '0'
        digits_after_decimal = max(0, total_digits - digits_before_decimal)
        return f'{num:.{digits_after_decimal}f}'
    
    list_nums =  [-100.145,  2.392, -34.827 , 4599.298, 0]
    
    for num in list_nums:
        print(flexible_format(num, total_digits=3))
    
    # -100
    # 2.39
    # -34.8
    # 4599
    # 0