Есть ли способ запретить округление в Python?

есть ли способ запретить округление Python, например:

>>> getcontext().prec = 11; print (Decimal(1.1237836550999999999)*10**10)
11237836551

Мне нужно показать первые 10 десятичных знаков десятичного числа, поэтому на самом деле мне нужно 11237836550 в качестве вывода, и мне не нужно это округление последней цифры (в данном случае 0 округляется до 1).

Иногда я могу просто увеличить точность (не в этом случае), а затем безопасно выполнить расчет, например:

>>> getcontext().prec = 14; print (Decimal(1.12378365509978)*10**10)
11237836550.998

Но предположим, что вы не знаете десятичную часть числа после этого '0', например это может быть 1.1237836550999999999999999999999999 ... и так далее, и тогда вы не можете полагаться на этот «обходной путь».

Итак, есть ли способ сообщить python «для этого вычисления я не хочу, чтобы выполнялось какое-либо округление, на самом деле я хочу, чтобы вы просто вернули мне необходимое количество десятичных знаков (10 в этом примере) и выбросили все еще после этого "?

Я заглянул сюда Усечение поплавков в Python, но в основном мне нужна не строка усеченное число с плавающей запятой, но другое десятичное число, используемое для выполнения вычислений. Кроме того, функция trunc принятого ответа в сообщении, которое я связал, не будет работать в этом случае, потому что десятичное число будет округлено непосредственно перед тем, как оно будет передано в аргумент функции 'f':

>>> def trunc(f, n):
...     '''Truncates/pads a float f to n decimal places without rounding'''
...     return ('%.*f' % (n + 1, f))[:-1]
... 
>>> trunc(Decimal(1.12378365509978), 10)
'1.1237836551' # and not 1.1237836550 as needed

Как я могу этого добиться?


person tonix    schedule 23.01.2015    source источник


Ответы (2)


Это то, что вы находите:

from decimal import *

getcontext().prec = 103

def precision(context, prec=100):

    return Decimal(context).quantize(Decimal('.'+'0'*(prec-1)+'1'), rounding=ROUND_DOWN)

print precision(Decimal(10).log10() / Decimal(2).log10())

Вывод:

3.3219280948873623478703194294893901758648313930245806120547563958159347766086252158501397433593701550
person dragon2fly    schedule 23.01.2015
comment
Это кажется более надежным решением, не так ли? - person tonix; 23.01.2015
comment
Как я могу использовать квантование с переменным результатом, то есть результатом выражения, Decimal(math.sqrt(2) + 5).quantize('.00000000001') дает TypeError: conversion from str to Decimal is not supported - person tonix; 23.01.2015
comment
@ user3019105 Внутри quantize находится Decimal, не забывайте об этом! In[5]: Decimal(math.sqrt(2) + 5).quantize(Decimal('.00000000001'), rounding=ROUND_DOWN) выдаст ваш результат без ошибки Out[5]: Decimal('6.41421356237') - person dragon2fly; 23.01.2015
comment
Как я могу использовать .quantize() с выражением типа: Decimal(Decimal(2).sqrt() + Decimal(5)).quantize(Decimal('.0000000000000000000000000000000000000001')), которое выводит Decimal('6.4142135623730950488016887242096980785697'), но ожидаемый результат будет Decimal('6.4142135623730950488016887242096980785696'), т.е. в последнем десятичном разряде будет 6, а не 7? - person tonix; 23.01.2015
comment
Вам необходимо указать метод округления, как показано в ответе: (Decimal(2).sqrt() + Decimal(5)).quantize(Decimal('.0000000000000000000000000000000000000001'), rounding=ROUND_DOWN) - person mata; 23.01.2015
comment
@ user3019105 На моем компьютере я не могу получить столько номеров после ., как у вас. Он дает мне только 27 цифр, поэтому я не могу проверить ваш случай. Но, как сказал @mata, не забывайте rounding=ROUND_DOWN, он должен выполнять свою работу. Я отредактировал свой ответ, чтобы вам не приходилось ломать глаза десятками 0 в quantize - person dragon2fly; 23.01.2015
comment
@mat, @dragonfly с квантованием ROUND_DOWN отлично работает для sqrt 2 + 5, но я также протестировал его с помощью этого выражения: getcontext().prec = 101; (Decimal(Decimal(10).log10() / Decimal(2).log10())).quantize(Decimal('.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'), rounding=ROUND_DOWN), которое является логарифмом с основанием 2 из 10 с его первыми 100 десятичными знаками. Даже с ROUND_DOWN он выводит ... (продолжение в комментарии ниже) - person tonix; 23.01.2015
comment
он выводит Decimal('3.3219280948873623478703194294893901758648313930245806120547563958159347766086252158501397433593701551'), в то время как точный вывод ... Decimal('3.3219280948873623478703194294893901758648313930245806120547563958159347766086252158501397433593701550'), поскольку 100-й десятичный знак log (10) / log (2) равен 0, что следует делать в таком случае? - person tonix; 23.01.2015
comment
@ user3019105 Вы должны указать getcontext().prec число, которое немного выше требуемой точности (например, 103), тогда оно должно работать. - person dragon2fly; 23.01.2015
comment
@ dragon2fly Да, повышение точности работает, но надежно ли это, даже если, например, после этого числа было бесконечное число 9s? Я знаю, что это выглядит маниакально, но мне нужно как можно больше универсального решения, могу ли я положиться на установку точности чуть выше и быть уверенным, что число будет таким, а не округленным, даже если десятичная часть после у него есть бесконечная серия девяток? Здесь помогает ROUND_DOWN? - person tonix; 23.01.2015
comment
@ user3019105 установите getcontext().prec = 200, попробуйте поставить prec=100, а затем prec=110 с вашим выражением выше. С prec=110 он дает ...55099..., а с prec=100 дает ...550. Поэтому я думаю, что это было бы надежно, независимо от того, сколько и какие цифры после pre-й цифры - person dragon2fly; 23.01.2015

Ваша проблема не в Decimal. Это float. А именно:

>>> 1.1237836550999999999
1.1237836551

Но если вы это сделаете:

>>> int(Decimal("1.1237836550999999999")*10**10)
11237836550

Тебе хорошо идти. Используйте строки при инициализации десятичных дробей с более высокой точностью.

person dmg    schedule 23.01.2015
comment
Хорошо, спасибо, всегда ли работает, даже если у меня число с бесконечными девятками? например 1.1237836550999999999999999999 .........? - person tonix; 23.01.2015
comment
Кажется, это не работает для >>> getcontext().prec = 11; print (int(Decimal(1.123783655099999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999)*10**10)), он все еще выводит 1.1237836551 - person tonix; 23.01.2015
comment
@ user3019105 у вас все еще отсутствуют кавычки. Используйте строку. Если вы напишете его как есть, оно преобразуется в число с плавающей запятой машинной точности перед передачей конструктору Decimal; числа с плавающей запятой двойной точности имеют точность около 15 десятичных знаков, поэтому все записанные вами 9 просто бесполезны. - person orion; 23.01.2015
comment
Понял, спасибо! Теперь он работает, но для чего здесь используется int (), похоже, он работает и без него: print (Decimal('1.12378365509999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999')*10**10) выводит 11237836550 - person tonix; 23.01.2015
comment
@ user3019105 Обратите внимание, что в своих примерах я использую оболочку python. Если вы хотите использовать это в сценарии, который выводит на стандартный вывод, вам понадобится print - person dmg; 23.01.2015
comment
Это работает, а как насчет int ()? Какая у этого цель? - person tonix; 23.01.2015
comment
print Decimal("1.1237836550999999999")*10**10 выходы, 11237836550.99999999900000000. Используя int, вы удалите нецелую часть. - person dmg; 23.01.2015
comment
Хорошо, спасибо за помощь, последний вопрос, как я могу заставить его работать, имея выражение? Я могу, например: >>> getcontext().prec = 41; print (Decimal( str( Decimal(2).sqrt() + Decimal(5) ))), который выводит 6.4142135623730950488016887242096980785697, но мне нужно вывести 6.4142135623730950488016887242096980785696, просто чтобы знать, как я могу использовать эти уловки с выражением с переменными? - person tonix; 23.01.2015
comment
@ user3019105 getcontext().prec = 41 + 1; print str(Decimal( str( Decimal(2).sqrt() + Decimal(5) )))[:-1], похоже, помогает - person dmg; 23.01.2015
comment
Он работает с 41 + 1, но не с 40 + 1, т.е. чтобы первые 39 десятичных знаков были правильными, getcontext().prec = 40 + 1; print (str(Decimal( str( Decimal(2).sqrt() + Decimal(5) )))) выводит 6.4142135623730950488016887242096980785697, а правильным будет 6.4142135623730950488016887242096980785696, есть ли способ в таком случае? - person tonix; 23.01.2015