Има ли начин да се попречи на закръгляването в 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.12378365509999999999999999999999999... и така нататък и тогава не можете да разчитате на това „заобиколно решение“.

И така, има ли начин да кажа на python „за това изчисление не искам да се извършва закръгляване, всъщност искам просто да ми върнете необходимия брой десетични знаци (10 в този пример) и да изхвърлите всичко друго след него"?

Разгледах тук Отрязване на плаващи елементи в Python, но основно това, от което се нуждая, не е низ от пресечен float, но друг десетичен знак, който да се използва за извършване на изчисления. Освен това функцията trunc на приетия отговор в публикацията, която свързах, няма да работи в този случай, тъй като Decimal ще бъде закръглен точно преди да бъде предаден на аргумента '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 след това '0'? Знам, че изглежда маниакално, но имам нужда от колкото е възможно повече универсално решение, мога ли да разчитам на настройката на точността само малко по-висока и да съм сигурен, че числото ще бъде това, а не закръглено, дори ако десетичната част след има серия от безкрайни 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
Добре, благодаря, работи ли винаги, дори ако имам номер с безкрайни 9? като 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