Python decimal accurate calculation

Several points frequently used
1. It can be passed to Decimal integer or string parameters, but it cannot be floating-point data, because floating-point data itself is not accurate. Need to convert floating point numbers to strings first

Incoming floating point number 5.55 and incoming string '5.55'

from decimal import *
print(Decimal(5.55)*100)
print(Decimal('5.55')*100)
print(Decimal('4.20') + Decimal('2.10'))
x = 4.20
y = 3.10
print(Decimal(str(x)) + Decimal(str(y)))
#运行结果分别是:
#554.9999999999999822364316060
#555.00
#6.30
#7.3

2. To convert from floating point data to Decimal type

from decimal import *
print(Decimal.from_float(22.222))
#运行结果:22.22200000000000130739863379858434200286865234375

3. Limit the result style by setting effective numbers:

from decimal import *
getcontext().prec = 5
print(Decimal(1)/Decimal(7))
print(Decimal(1.00)/Decimal(3.0))
#运行结果,五个有效数字:0.14286
#运行结果:0.33333

Of course, while the accuracy is improved, it will definitely bring a loss of performance. In occasions that require extremely precise data (such as financial settlement), these performance losses are worthwhile. But if it is a large-scale scientific calculation, operating efficiency needs to be considered. After all, the native float is definitely much faster than the Decimal object.

4. Round to five decimal places

from decimal import *
print(Decimal('50.5679').quantize(Decimal('0.00')))
#运行结果四舍五入保留了两位小数:50.57

5. Decimal result is converted to string

from decimal import *
print(str(Decimal('3.40').quantize(Decimal('0.0'))))
#运行结果:3.4

Because the article is forwarded, the result of my execution is not str, which is yet to be studied

Special attention: prec is the effective number length.
If the length of prec is smaller than the length of the number, the number obtained by *100 is wrong

from decimal import *
print(getcontext())  # Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
num,num1 = '12355','123.55'
getcontext().prec = len(num) +2
print(Decimal(num1)*100 == Decimal(num))   #True
getcontext().prec = 3
# todo 如果prec的长度比数字的长度小的话,*100得出的数就不对了
print(Decimal(num1)*100)                   #1.24E+4
print(Decimal(num1))                       #123.55
print(Decimal(num1)*100 == Decimal(num))   #False
print(Decimal(num))                        #12355

6. The explanation and behavior of each parameter of python decimal.quantize() parameter rounding.
I actually read a wave of documents because I wondered about the performance difference between ROUND_FLOOR and ROUND_DOWN, but I felt like I pulled out a ticket that I hadn't noticed before.

Post an explanation in the decimal document:

ROUND_CEILING (towards Infinity),
ROUND_DOWN (towards zero),
ROUND_FLOOR (towards -Infinity),
ROUND_HALF_DOWN (to nearest with ties going towards zero),
ROUND_HALF_EVEN (to nearest with ties going to nearest even integer),
ROUND_HALF_UP (to nearest with ties going away from zero), or
ROUND_UP (away from zero).
ROUND_05UP (away from zero if last digit after rounding towards zero would have been 0 or 5; otherwise towards zero)

It is very abstract to read the above explanation directly. Let me explain with examples what kind of behavior they have when the positive and negative numbers are different.

1) First, give a group of negative numbers whose last digit exceeds 5:

from decimal import *

x = Decimal('-3.333333333') + Decimal('-2.222222222')
print(x)   # -5.555555555
print(x.quantize(Decimal('1.0000'), ROUND_HALF_EVEN))    # -5.5556
print(x.quantize(Decimal('1.0000'), ROUND_HALF_DOWN))    # -5.5556
print(x.quantize(Decimal('1.0000'), ROUND_CEILING))      # -5.5555
print(x.quantize(Decimal('1.0000'), ROUND_FLOOR))        # -5.5556
print(x.quantize(Decimal('1.0000'), ROUND_UP))           # -5.5556
print(x.quantize(Decimal('1.0000'), ROUND_DOWN))         # -5.5555

ROUND_HALF_EVEN and ROUND_HALF_DOWN: EVEN is the default setting value of quansize, which can be obtained by getcontext(), EVEN is rounded to one digit, and DOWN is one digit to the nearest 0.

ROUND_CEILING and ROUND_FLOOR: There is no rounding for CEILING over 5 because it tends to be positive infinity, and FLOOR is rounded up in order to always become smaller.

ROUND_UP and ROUND_DOWN: UP is always carried, and DOWN is never carried.

2) Compare a group of data whose last one does not exceed 5:

from decimal import *

x = Decimal('-3.333333333') + Decimal('-1.111111111')
print(x)   # -4.444444444
print(x.quantize(Decimal('1.0000'), ROUND_HALF_EVEN))    # -4.4444
print(x.quantize(Decimal('1.0000'), ROUND_HALF_DOWN))    # -4.4444
print(x.quantize(Decimal('1.0000'), ROUND_CEILING))      # -4.4444
print(x.quantize(Decimal('1.0000'), ROUND_FLOOR))        # -4.4445
print(x.quantize(Decimal('1.0000'), ROUND_UP))           # -4.4445
print(x.quantize(Decimal('1.0000'), ROUND_DOWN))         # -4.4444

ROUND_HALF_EVEN and ROUND_HALF_DOWN: EVEN is the default setting value of quansize, which can be obtained through getcontext(). EVEN is not rounded because it cannot be rounded, and DOWN is also not rounded.

ROUND_CEILING and ROUND_FLOOR: CEILING tends to be infinitely non-carrying. Even if FLOOR does not exceed 5, it will be a bit in order to always become smaller.

ROUND_UP and ROUND_DOWN: UP is always carried, and DOWN is never carried.

3) The case where the number behind the positive part is greater than 5:

from decimal import *

x = Decimal('3.333333333') + Decimal('2.222222222')
print(x)   # 5.555555555
print(x.quantize(Decimal('1.0000'), ROUND_HALF_EVEN))    # 5.5556
print(x.quantize(Decimal('1.0000'), ROUND_HALF_DOWN))    # 5.5556
print(x.quantize(Decimal('1.0000'), ROUND_CEILING))      # 5.5556
print(x.quantize(Decimal('1.0000'), ROUND_FLOOR))        # 5.5555
print(x.quantize(Decimal('1.0000'), ROUND_UP))           # 5.5556
print(x.quantize(Decimal('1.0000'), ROUND_DOWN))         # 5.5555

ROUND_HALF_EVEN and ROUND_HALF_DOWN: EVEN is the default setting value of quansize, which can be obtained through getcontext(). EVEN is rounded up because it is rounded, and DOWN is also rounded up.

ROUND_CEILING and ROUND_FLOOR: CEILING positive numbers always carry, and FLOOR will never carry positive numbers.

ROUND_UP and ROUND_DOWN: UP is always carried, and DOWN is never carried.

4) When the number behind the positive part is less than 5:

from decimal import *

x = Decimal('3.333333333') + Decimal('1.111111111')
print(x)   # 4.444444444
print(x.quantize(Decimal('1.0000'), ROUND_HALF_EVEN))    # 4.4444
print(x.quantize(Decimal('1.0000'), ROUND_HALF_DOWN))    # 4.4444
print(x.quantize(Decimal('1.0000'), ROUND_CEILING))      # 4.4445
print(x.quantize(Decimal('1.0000'), ROUND_FLOOR))        # 4.4444
print(x.quantize(Decimal('1.0000'), ROUND_UP))           # 4.4445
print(x.quantize(Decimal('1.0000'), ROUND_DOWN))         # 4.4444

ROUND_HALF_EVEN and ROUND_HALF_DOWN: EVEN is the default setting value of quansize, which can be obtained through getcontext(). EVEN is not rounded because it has not reached the rounding value, and DOWN is also not rounded.

ROUND_CEILING and ROUND_FLOOR: CEILING positive numbers always carry, and FLOOR will never carry positive numbers.

ROUND_UP and ROUND_DOWN: UP is always carried, and DOWN is never carried.

Summary:
In fact, through the above set of examples, we can find that the behavior of positive numbers is very predictable and very simple. The situation of negative numbers is slightly more complicated. Some functions are designed to be used in certain situations as negative numbers. The difference between ROUND_DOWN and ROUND_FLOOR that cannot be reproduced in positive numbers, ROUND_DOWN is no matter whether it is greater than 5 or not, it will not remain as it is, and Floor behaves in the same way in positive numbers, but in negative numbers, it tends to be infinitely small, so whether it is greater than 5. He will become smaller and carry. Instead, the behavior of ROUND_UP and ROUND_DOWN is the most predictable, that is, no matter the size of the following number, UP will carry, and DOWN will never carry.

Reference: https://blog.csdn.net/weixin_37989267/article/details/79473706

Guess you like

Origin blog.csdn.net/zhaoweiya/article/details/108726201