0

I have to export numbers as text using python and experience several ways of not doing what i want. There is string.format(), old % string format, and F-Strings - plenty of very smart ways of formatting things into strings, but I can't find a simple way to produce a string from a number that meets my criteria of having:

  • Decimal separator from locale.
  • No thousand separator.
  • Infinite (~) number of significant integer digits, but at least one zero.
  • Specified number of significant decimal digits - with no decimal separator if no significant decimals.
  • No scientific notation (e-crap).

Not a very exotic request? Have I missed something obvious?

Thants
  • 1
  • 1
  • 2
    who said that there must be a simple way? can you please share examples for each criteria – Nir Elbaz Apr 02 '22 at 10:31
  • Use `round(float_number, number_of_decimals)` and then put it in string. – BokiX Apr 02 '22 at 10:37
  • Well, I'm looking for a simple way, that is not writing the formatter myself. - Decimal separator from locale: . or , depending on the locale setting. - No thousand separator: 12345 (not 12,345 or 12 345 for example) - Infinite (~) number of significant integer digits, but at least one zero: 0 or 0.999999999 or 1234567890.99 for example Specified number of significant decimal digits - with no decimal separator if no significant decimals: 1.1 with 2 decimals: 1.1 | 1.11111 with 2 decimals: 1.11 | 1.1 with 0 decimals: 1 No scientific notation (e-crap): 0.00005 with 2 decimals: 0 (not 5e-05) – Thants Apr 02 '22 at 10:43
  • @BokiX: locale.str(round(aNumber, 5)) gives scientific notation result from time to time. – Thants Apr 02 '22 at 10:48
  • Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking. – Community Apr 02 '22 at 10:58

2 Answers2

0

Using locale, floor and f-strings it is quite easy to make a string like you want:

import locale
locale.getdefaultlocale()
from math import floor
a = 12303123895.281789274918
a_string = f"{floor(a)}{locale.localeconv()['decimal_point']}"+f"{a-floor(a):.1g}"[2:]

print(f"a = {a_string}")

This gives me:

a = 12303123895.3

because my locale decimal separator is "." and you can set the number of decimals you want by changing the ":.1g" to ":.Xg", X being the number of decimals you want.

Logi
  • 26
  • 4
  • Thanks but no, it will still give me the scientific notation results, like '80.-10'. And you will have to trim the separator when there are no significant decimals. a=0 Gives '0.' – Thants Apr 02 '22 at 11:04
0

Yup, I looked up a 30 years old Smalltalk implementation. The translation was pretty straight forward but I was not able to extend the Python Number class (as natural in Smalltalk).

def intlPrintRoundedMax(aNumber, decs, insigDecBool, thousBool, lZeroBool, positiveSignBool) :
    """Answer a string, the ASCII representation of the receiver rounded to decs decimal 
     places using delimiters from locale.
     If <insigDecBool> is false only append significant decimals.
     If <thousBool> is true, insert delimiter for thousands.
     If <lZeroBool> is true, a leading zero is printed.
     If <positiveSignBool> add sign to positive numbers"""   
    """920416  TA  c"""

locales = locale.localeconv()
sDecimal = locales['decimal_point']
sThousand = locales['thousands_sep']
sPositiveSign = locales['positive_sign'] if positiveSignBool else ''
sNegativeSign = locales['negative_sign']

answer = ''
rounder = 10 ** decs

value = round(aNumber * rounder)
if (value < 0) :
    answer += sNegativeSign
    value = 0 - value
else :
    if (value != 0) : answer += sPositiveSign

if (lZeroBool) :
    divisor = 10 * rounder
else :
    if (value == 0) : 
        answer += '0' 
        return answer
    divisor = rounder

while (divisor <= value) :
    divisor = divisor * 10

if (thousBool) :
    thousands = thousandRounders = 1000 * rounder
    while (thousands <= value) :
        thousands = thousands * 1000
    thousands = max((thousands // 1000), thousandRounders)
else : thousands = None


if (divisor == rounder) : answer += sDecimal
divisor = divisor // 10
while (divisor > 1) :
    answer += chr(value // divisor + 48)

    value = value % divisor

    if ((not insigDecBool) and ((value == 0) and (divisor <= rounder))) :
        return answer

    if (divisor == thousands) :
        thousands = max((thousands // 1000), thousandRounders)
        answer += sThousand

    if (divisor == rounder) : answer += sDecimal
    divisor = divisor // 10

answer += chr(value + 48)
return answer

Thants
  • 1
  • 1