Хорошо представляя число с плавающей запятой в Python

Я хочу представить число с плавающей запятой как строку, округленную к некоторому числу значащих цифр и никогда использованию экспоненциального формата. По существу я хочу отобразить любое число с плавающей запятой и удостовериться, что это “выглядит хорошим”.

Существует несколько частей к этой проблеме:

  • Я должен смочь указать число значащих цифр.
  • Число значащих цифр должно быть переменным, который не может быть, покончили со строковым оператором форматирования. [редактирование] я был исправлен; строковый оператор форматирования может сделать это.
  • Мне нужен он, чтобы быть округленным способ, которым человек ожидал бы, не что-то как 1,999999999999

Я выяснил один способ сделать это, хотя он похож на работу - вокруг, и это не совсем прекрасно. (Максимальная точность составляет 15 значащих цифр.)

>>> def f(number, sigfig):
    return ("%.15f" % (round(number, int(-1 * floor(log10(number)) + (sigfig - 1))))).rstrip("0").rstrip(".")

>>> print f(0.1, 1)
0.1
>>> print f(0.0000000000368568, 2)
0.000000000037
>>> print f(756867, 3)
757000

Существует ли лучший способ сделать это? Почему Python не имеет встроенной функции для этого?

12
задан dln385 19 April 2010 в 00:54
поделиться

3 ответа

Похоже, что нет встроенного трюка с форматированием строк, который позволяет (1) печатать числа с плавающей запятой, первая значащая цифра которых стоит после 15-го десятичного знака, и (2) не в экспоненциальной нотации. Таким образом, остается ручное управление строкой.

Ниже я использую модуль decimal для извлечения десятичных цифр из числа с плавающей запятой. Функция float_to_decimal используется для преобразования числа с плавающей запятой в десятичное объект. Очевидный способ decimal.Decimal (str (f)) неверен, потому что str (f) может потерять значащие цифры.

float_to_decimal был взят из документации модуля decimal .

После получения десятичных цифр в виде кортежа целых чисел, приведенный ниже код делает очевидную вещь: отрезает желаемое количество значащих цифр, округляет, если необходимо, объединяет цифры в строку, прикрепляет знак, поместите десятичную точку и нули слева или справа в зависимости от ситуации.

Внизу вы найдете несколько случаев, которые я использовал для тестирования функции f .

import decimal

def float_to_decimal(f):
    # http://docs.python.org/library/decimal.html#decimal-faq
    "Convert a floating point number to a Decimal with no loss of information"
    n, d = f.as_integer_ratio()
    numerator, denominator = decimal.Decimal(n), decimal.Decimal(d)
    ctx = decimal.Context(prec=60)
    result = ctx.divide(numerator, denominator)
    while ctx.flags[decimal.Inexact]:
        ctx.flags[decimal.Inexact] = False
        ctx.prec *= 2
        result = ctx.divide(numerator, denominator)
    return result 

def f(number, sigfig):
    # http://stackoverflow.com/questions/2663612/nicely-representing-a-floating-point-number-in-python/2663623#2663623
    assert(sigfig>0)
    try:
        d=decimal.Decimal(number)
    except TypeError:
        d=float_to_decimal(float(number))
    sign,digits,exponent=d.as_tuple()
    if len(digits) < sigfig:
        digits = list(digits)
        digits.extend([0] * (sigfig - len(digits)))    
    shift=d.adjusted()
    result=int(''.join(map(str,digits[:sigfig])))
    # Round the result
    if len(digits)>sigfig and digits[sigfig]>=5: result+=1
    result=list(str(result))
    # Rounding can change the length of result
    # If so, adjust shift
    shift+=len(result)-sigfig
    # reset len of result to sigfig
    result=result[:sigfig]
    if shift >= sigfig-1:
        # Tack more zeros on the end
        result+=['0']*(shift-sigfig+1)
    elif 0<=shift:
        # Place the decimal point in between digits
        result.insert(shift+1,'.')
    else:
        # Tack zeros on the front
        assert(shift<0)
        result=['0.']+['0']*(-shift-1)+result
    if sign:
        result.insert(0,'-')
    return ''.join(result)

if __name__=='__main__':
    tests=[
        (0.1, 1, '0.1'),
        (0.0000000000368568, 2,'0.000000000037'),           
        (0.00000000000000000000368568, 2,'0.0000000000000000000037'),
        (756867, 3, '757000'),
        (-756867, 3, '-757000'),
        (-756867, 1, '-800000'),
        (0.0999999999999,1,'0.1'),
        (0.00999999999999,1,'0.01'),
        (0.00999999999999,2,'0.010'),
        (0.0099,2,'0.0099'),         
        (1.999999999999,1,'2'),
        (1.999999999999,2,'2.0'),           
        (34500000000000000000000, 17, '34500000000000000000000'),
        ('34500000000000000000000', 17, '34500000000000000000000'),  
        (756867, 7, '756867.0'),
        ]

    for number,sigfig,answer in tests:
        try:
            result=f(number,sigfig)
            assert(result==answer)
            print(result)
        except AssertionError:
            print('Error',number,sigfig,result,answer)
8
ответ дан 2 December 2019 в 21:23
поделиться

Если вам нужна точность с плавающей запятой, вам необходимо использовать модуль decimal , который является частью стандартной библиотеки Python :

>>> import decimal
>>> d = decimal.Decimal('0.0000000000368568')
>>> print '%.15f' % d
0.000000000036857
6
ответ дан 2 December 2019 в 21:23
поделиться

Для правильного ответа на этот вопрос необходимы поплавки произвольной точности. Поэтому использование десятичного модуля является обязательным. Не существует метода преобразования десятичной дроби в строку без использования экспоненциального формата (часть исходного вопроса), поэтому я написал функцию для этого:

def removeExponent(decimal):
    digits = [str(n) for n in decimal.as_tuple().digits]
    length = len(digits)
    exponent = decimal.as_tuple().exponent
    if length <= -1 * exponent:
        zeros = -1 * exponent - length
        digits[0:0] = ["0."] + ["0"] * zeros
    elif 0 < -1 * exponent < length:
        digits.insert(exponent, ".")
    elif 0 <= exponent:
        digits.extend(["0"] * exponent)
    sign = []
    if decimal.as_tuple().sign == 1:
        sign = ["-"]
    print "".join(sign + digits)

Проблема заключается в попытке округления до значащих цифр. Метод «quantize ()» Decimal не округляет значения выше десятичной точки, а функция «round ()» всегда возвращает число с плавающей запятой. Я не знаю, являются ли это ошибками, но это означает, что единственный способ округлить числа с плавающей запятой бесконечной точности - это проанализировать их как список или строку и выполнить округление вручную. Другими словами, на этот вопрос нет вменяемого ответа.

-1
ответ дан 2 December 2019 в 21:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: