Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/giocip/num7
SUPREME PRECISION GENERAL PURPOSE ARITHMETIC-LOGIC DECIMAL CLASS
https://github.com/giocip/num7
arbitrary-precision arithmetic decimal financial floating-point ieee754 math micropython precision python raspberry school-education
Last synced: about 1 month ago
JSON representation
SUPREME PRECISION GENERAL PURPOSE ARITHMETIC-LOGIC DECIMAL CLASS
- Host: GitHub
- URL: https://github.com/giocip/num7
- Owner: giocip
- License: mit
- Created: 2022-12-08T16:14:13.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2024-08-09T08:58:05.000Z (5 months ago)
- Last Synced: 2024-11-14T14:57:43.471Z (about 1 month ago)
- Topics: arbitrary-precision, arithmetic, decimal, financial, floating-point, ieee754, math, micropython, precision, python, raspberry, school-education
- Language: Python
- Homepage:
- Size: 240 KB
- Stars: 1
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# SUPREME PRECISION GENERAL PURPOSE ARITHMETIC-LOGIC DECIMAL CLASS
## _DESCRIPTION AND DOC_- _**`Num`**_ is a lightweight floating point numeric class for arbitrary precision results with always supreme precision.
Easy to use like school math and WITHOUT IEEE754 ISSUES or -0 FAILURES, it can be deployed
for web e-commerce developing, accounting apps and general math programs included financial ones.
Compatible with MicroPython also a Rasperry pi pico (RP2040) can work with almost num7 capability.---
## Installation num7 package
### Using PIP
- To install _**`num7 package`**_ using `pip`, enter the following:
```python
pip install num7 #win
pip3 install num7 #linux
```- Ok!
---
## HOW TO USE (integer numeric strings (ex. '2.0') MUST BE SUFFIXED WITH .0):
--- CALCULATOR MODE --->>> from num7 import Num, Num as calc
ADDITION: >>> calc.add('-5.3', '2.1') #Num('-3.2')
SUBTRACTION: >>> calc.sub('-5.3', '2.1') #Num('-7.4')
MULTIPLICATION: >>> calc.mul('-5.3', '2.1') #Num('-11.13')
DIVISION: >>> calc.div('-5.3', '2.1') #Num('-2.52380952380952380952380952380952380952380952380952380952380952380952380952380952')
M+: >>> M = calc('0.0'); M.inc('3.0'); M.inc('3.3'); M.inc('3.7'); print(M) #10.0
M-: >>> M.dec('5.0'); M.dec('3.3'); M.dec('1.5'); print(M) #0.2
MC: >>> M.clear(); print(M) #0.0
INT DIV AND REM: >>> calc.divmod('5.0', '3.0') #(Num('1.0'), Num('2.0')) => tuple
FLOAT DIV AND REM: >>> calc.divmod('5.2', '3.1') #(Num('1.0'), Num('2.1')) => tuple
POWER: >>> calc.pow('-5.3', '2.0') #Num('28.09')
SQRT: >>> calc.sqrt('2.0') #Num('1.41421356237309504880168872420969807856967187537694807317667973799073247846210703')
ROOT_ith >>> calc.root_i('1.860867', 3) #Num('1.23')
ROUND: >>> calc.sqrt('2.0').round(2) #Num('1.41')
ABSOLUTE VALUE >>> calc.abs('-3.0') #Num('3.0')
SUM: >>> cart = ['19.32','18.37','15.13']; calc.sum(*cart) #Num('52.82')
MEAN: >>> cart = ['19.32','18.37','15.13']; calc.mean(*cart).round() #Num('17.61')
MIN: >>> cart = ['19.32','18.37','15.13']; calc.min(cart) #Num('15.13')
MAX: >>> cart = ['19.32','18.37','15.13']; calc.max(cart) #Num('19.32')
EXP: >>> calc.mul('-5.3e1024', '2.1e1024').num2exp() #'-1.113e2049'
REPL: >>> a = calc('0.1'); b = calc('0.2'); print(calc.add(a, b)) #0.3## CODING:
>>> from num7 import Num, Num as calc(=) assignment:
>>> a = Num('3.0'); b = Num('5.0'); c = Num('0.0'); #
>>> print('a =', a, 'b =', b, 'c =', c) #a = 3.0 b = 5.0 c = 0.0(+) adding:
>>> R = a+b+c; print(R) #8.0
>>> a = Num('0.1'); b = Num('0.2'); c = Num('0.0'); print(a+b+c) #0.3(-) subtracting:
>>> a = Num('0.1'); b = Num('0.2'); c = Num('0.3');
>>> print(a+b-c) #0.0
>>> R = Num('-3.99') - Num('-5.20') - Num('+3.01'); print(R) #-1.8(*) multiplying:
>>> Num('-3.99') * Num('-5.20') * Num('+3.01') #-3.99 * (-5.20) * (+3.01 ) = Num('62.45148')
(/) dividing (80 decimal digits default gets only for division operation):
>>> Num('3.0') / Num('5.7') #3 : 5.7 = Num('0.52631578947368421052631578947368421052631578947368421052631578947368421052631578')
Division precision (ex. 128 decs) may be specified as parameter after numeric string as:
>>> Num('3.0', 128) / Num('5.7', 128) #3 : 5.7 = Num('0.52631578947368421052631578947368421052631578947368421052631578947368421052631578947368421052631578947368421052631578947368421052')(// % operators, divmod python3 built-in function) int division and remainder:
>>> a = Num('5.0'); b = Num('2.0') #
>>> Q = a // b; R = a % b; print('Quotient =', Q, 'Remainder =', R) #Quotient = 2.0 Remainder = 1.0
>>> a = Num('15.0'); b = Num('4.0') #
>>> Q, R = divmod(a, b); print('Quotient =', Q, 'Remainder =', R) #Quotient = 3.0 Remainder = 3.0(divmod python3 built-in function) floating division and remainder:
>>> a = Num('10.123456789'); b = Num('2.0') #
>>> Q, R = divmod(a, b); print('Quotient =', Q, 'Remainder =', R) #Quotient = 5.0 Remainder = 0.123456789(sqrt) square root function:
>>> a = Num('123_456_789.1234567890123456789'); root = a.sqrt() # Num('11111.11106611111096998611053449930232404576951925017079015206589094347963821409843324')
>>> print('result digits number tuple =>', root.len()) #result digits number tuple => (5, 80)(**) power operator and pow python3 built-in function:
>>> a = Num('2.22123') ** 64; print(a) # 15204983311631674774944.65147209888660757554174463321311015807893679105748958794491681177995203669698667160837739445605536688871012507194541849848681968140805876570485027380472936734094801420552285940765338219588362327695177798251793912104057999943308320501195784173135380826413054938730768027747418766018606636039075568645106645889100039914241
>>> print(a.len()) #(23, 320) digits len tuple
>>> print(Num(Num.pi)) #3.141592654
>>> pow(Num(Num.pi), 8) #Num('9488.531025982131642534428505085353941520356351078169077371202330414440366336')logic (in, not in, is, is not, <, <=, >, >=, !=, ==) and relational operators (and, or, not).
(in):
>>> L = [Num('0.1'), Num('1.0'), Num('5.5'), Num('-3.0'), Num('-2.9'), Num('-3.0001'), Num('2.2')]
>>> Num('-3.0001') in L; Num('-3.00001') in L #True False(not in):
>>> Num('-3.0001') not in L; Num('-3.00001') not in L #False True
(is, is not):
>>> M = calc('0.0'); Num('0.0') is M #False
>>> M = calc('0.0'); M.inc('0.1') is not M; M #True Num('0.1')
>>> M; N = M; N.dec('0.1'); N is M; M; N # Num('0.1') True Num('0.0') Num('0.0')(< <= > >= != ==)
>>> a = Num('0.0'); b = Num('0.1'); c = Num('-0.2')
>>> a < b; a < c; b < c #True False False
>>> a <= a; a <= c; b <= c #True False False
>>> a > b; a > c; b > c #False True True
>>> a >= a; a >= c; b >= c #True True True
>>> c == -2*b; a == c + 2*b ; a != a+b+c #True True True
>>> a and b; a or b; not a #Num('0.0') Num('0.1') True
>>> True if a and b else False #False
>>> True if a or b else False #True(+ - unary operators)
>>> Num('+2.5521') # Num('2.5521')
>>> Num('-3.3321') # Num('-3.3321')
>>> Num('+2.5521') + Num('-3.3321') #Num('-0.78')bitwise operators code:
from num7 import Num
print('--- (&) AND ---')
op1 = Num('3.0')
op2 = 5
print(f'{int(op1):08b}', op1) #00000011 3.0
op1 &= op2 #AND
print(f'{op2:08b}', op2) #00000101 5
print(f'{int(op1):08b}', op1) #00000001 1print('--- (|) OR ---')
op1 = Num('3.0')
op2 = 5
print(f'{int(op1):08b}', op1) #00000011 3.0
op1 |= op2 #OR
print(f'{op2:08b}', op2) #00000101 5
print(f'{int(op1):08b}', op1) #00000111 7print('--- (^) XOR ---')
op1 = Num('3.0')
op2 = 5
print(f'{int(op1):08b}', op1) #00000011 3.0
op1 ^= op2 #XOR
print(f'{op2:08b}', op2) #00000101 5
print(f'{int(op1):08b}', op1) #00000110 6print('--- (<<) LEFT SHIFT -X10 MULTIPLIER ---')
op1 = Num('1.0')
op2 = 2
print(f'{int(op1):08b}', op1) #00000001 1.0
op1 <<= op2 #LEFT SHIFT -X10 MULTIPLIER
print(f'{op2:08b}', op2) #00000010 2
print(f'{int(op1):08b}', op1) #01100100 100.0print('--- (>>) RIGHT SHIFT -X10 DIVIDER ---')
op1 = Num('250.0')
op2 = 1
print(f'{int(op1):08b}', op1) #11111010 250.0
op1 >>= op2 #RIGHT SHIFT -X10 DIVIDER
print(f'{op2:08b}', op2) #00000001 1
print(f'{int(op1):08b}', op1) #00011001 25.0print('--- (~) NOT ---')
op1 = Num('10.0')
print(f'{int(op1):08b}', op1) #00001010 10.0
op2 = ~op1 #(~) NOT
print(f'{int(op2):08b}', op2) #00000101 5.0On a given variable the following arithmetic methods are available:
#variable arithmetics
from num7 import Num
a = Num('10.25')
print(a) #10.25
a.inc() #increment (default) by one
print(a) #11.25
a.dec(2) #decrement (optional) 2 units
print(a) #9.25
a.incmul() #multiply (default) 10 times
print(a) #92.5
a.decdiv(100) #x100 (optional) division
print(a) #0.925
a.clear() #a variable set to zero
print(a) #0.0EVEN ODD numbering methods:
from num7 import Num
a = Num(6); b = Num(3); c = Num('3.14')
print(a, 'INTEGER =>', a.is_numint(), 'EVEN =>', a.is_numeven()) #6.0 INTEGER => True EVEN => True
print(b, 'INTEGER =>', b.is_numint(), 'ODD =>', b.is_numodd()) #3.0 INTEGER => True ODD => True
print(c, 'FLOAT =>', c.is_numfloat()) #3.14 FLOAT => True# Advanced logic programming snippet
LOOP EXAMPLE >>>
from num7 import Num
i = Num(0)
while i < Num('1.0'):
i.inc('0.1') #i += Num('0.1')
if i <= Num('0.5'):
continue
print(i) #0.6, 0.7, 0.8, 0.9, 1.0
while i:
i.dec('0.1') #i -= Num('0.1')
if i >= Num('0.5'):
continue
print(i) #0.4 0.3 0.2 0.1 0.0ROUNDING AND ACCOUNTING >>>
from num7 import Num
p = Num('11.19') #PRICE -Toslink cable for soundbar
pd = round(p.f_price_over(-7)) #PRICE DISCOUNTED 7%
d = round(p - pd) #DISCOUNT
p_noTAX = round(p.f_price_spinoff(22)) #ITEM COST WITHOUT TAX 22%
TAX = round(p - p_noTAX) #TAX 22%
print(F'price={p} PAYED={pd} discount={d} COST={p_noTAX} TAX={TAX}') #price=11.19 PAYED=10.41 discount=0.78 COST=9.17 TAX=2.02OUTPUT FORMATTING AND LOCALIZATION >>>
import locale
from num7 import Num
s = locale.setlocale(locale.LC_ALL, "")
print('settings:', s) #settings: Italian_Italy.1252
#calculating banking loan
asset = Num('100_000.0'); rate = Num('6.5'); years = Num('20.0')
monthly_payment = Num.f_fund_fr(asset, rate, years)
print(locale.format_string("%.2f", float(monthly_payment))) #756,30
print(locale.currency(float(monthly_payment), grouping=True)) #756,30 (currency symbol)ROUNDING TYPES >>>
from num7 import Num
''' Num floor rounding '''
print('--' * 10 + ' Num floor rounding')
n = Num(Num.pi) # 3.141592654
print(n, n.round_floor(2)) # 3.14
n = -Num(Num.pi) #-3.141592654
print(n, n.round_floor(2)) #-3.15
n = Num(Num.pi) - 3 # 0.141592654
print(n, n.round_floor(2)) # 0.14
n = -Num(Num.pi) + 3 #-0.141592654
print(n, n.round_floor(2)) #-0.15print('--' * 10 + ' Num ceil rounding')
''' Num ceil rounding '''
n = Num(Num.pi) # 3.141592654
print(n, n.round_ceil(2)) # 3.15
n = -Num(Num.pi) #-3.141592654
print(n, n.round_ceil(2)) #-3.14
n = Num(Num.pi) - 3 # 0.141592654
print(n, n.round_ceil(2)) # 0.15
n = -Num(Num.pi) + 3 #-0.141592654
print(n, n.round_ceil(2)) #-0.14print('--' * 10 + ' Num standard rounding')
''' Num standard rounding '''
n = Num(Num.pi) # 3.141592654
print(n, n.round()) # 3.14
n = -Num(Num.pi) #-3.141592654
print(n, n.round()) #-3.14
n = Num(Num.pi) - 3 # 0.141592654
print(n, n.round(4)) # 0.1416
n = -Num(Num.pi) + 3 #-0.141592654
print(n, n.round(4)) #-0.1416print('--' * 10 + ' Num half to even rounding (statistic, zero symmetric)')
''' Num half even rounding '''
n = Num(Num.pi).round_floor(4) # 3.1415
print(n, n.round_bank(3)) # 3.142
n = -Num(Num.pi).round_floor(4) #-3.1415
print(n, n.round_bank(3)) #-3.142
n = Num(Num.pi).round_floor(8) - 3 # 0.14159265
print(n, n.round_bank(7)) # 0.1415926
n = -Num(Num.pi).round_floor(8) + 3 #-0.14159265
print(n, n.round_bank(7)) #-0.1415926PERFORMANCE EVALUATION AND SQUARENESS >>>
from num7 import Num
from time import perf_counter
tic = perf_counter() #Start Time
a = Num('-1.123456789'+'e-100') #calculating division 10**100...
toc = perf_counter() #End Time
T1 = toc - tic
print(f"a finished sec. {T1:1.6f}")
tic = perf_counter() #Start Time
b = ('-1.123456789') >> Num('100.0') #calculating division 10**100...
toc = perf_counter() #End Time
T2 = toc - tic
print(f"b finished sec. {T2:1.6f}")
R = Num.f_perf_time(str(T1), str(T2))
print('PCT=>', R[0].round(), 'SCALE=>', R[1].round(), 'SQUARENESS=>', a == b) #PCT= -98.6 SCALE= -70.47 SQUARENESS=> True
#stock exchange assets performance
previous = Num('26.96'); now = Num('27.27')
var_pct = Num.f_perf(previous, now).round()
print(f'{float(var_pct):+.2f}')SCIENTIFIC NOTATION AND HIGH PRECISION RESULTS >>>
from num7 import Num
a = Num('1_000_000_000_000_000_000_000.0') #standard notation
b = Num('1e21') #scientific notation
SUM = a + b #SUM
ieee754 = float(a)+float(b)
print('SUM == ieee754', SUM == Num(str(ieee754)), ' SUM =>', SUM.num2exp()) #SUM == ieee754 True SUM => 2.0e21
a = Num('1_000_000_000_000_000_000_000.0') #standard notation
b = Num('1e21') #scientific notation
MUL = a * b #MUL
ieee754 = float(a)*float(b)
print('MUL == ieee754', MUL == Num(str(ieee754)), ' MUL =>', MUL.num2exp()) #MUL == ieee754 True MUL => 1.0e42
a = '1.23456789'
b = '9.87654321'
MUL = Num(a) * Num(b) #MUL
ieee754 = float(a)*float(b)
print('MUL == ieee754', MUL == Num(str(ieee754)), 'MUL =>', MUL, float(a)*float(b), '=> IEEE754 PRECISION FAILURE!') #MUL == ieee754 False MUL => 12.1932631112635269 12.193263111263525 => IEEE754 PRECISION FAILURE!
a = '1.23456789e320' #scientific notation
b = '9.87654321e320'
MUL = Num(a) * Num(b) #MUL
ieee754 = float(a)*float(b)
print('MUL == ieee754', MUL.str() == str(ieee754), 'MUL =>', MUL.num2exp(), float(a)*float(b), '=> IEEE754 inf FAILURE!') #MUL == ieee754 False MUL => 1.21932631112635269e641 inf => IEEE754 inf FAILURE!
a = '2e320' #scientific notation
b = '3e-320'
MUL = Num(a) * Num(b) #MUL
ieee754 = float(a)*float(b)
print('MUL == ieee754', MUL.str() == str(ieee754), 'MUL =>', MUL.num2exp(), ieee754, '=> IEEE754 inf FAILURE!') #MUL == ieee754 False MUL => 6.0 inf => IEEE754 inf FAILURE!
a = '1e200' #scientific notation
b = '5e1200'
T1 = Num(a, 1200) #ultra precision (over 80 digits default) floating point division must be specified!
T2 = Num(b)
DIV = T1 / T2 #DIV
ieee754 = float(a)/float(b)
print('DIV == ieee754', DIV.str() == str(ieee754), 'DIV =>', DIV.num2exp(), ieee754, '=> IEEE754 precision FAILURE!') #DIV == ieee754 False DIV => 2.0e-1001 0.0 => IEEE754 precision FAILURE!FLOAT TO NUM CONVERSION LIST >>>
from num7 import Num
L = [1011, 0.0, 9.998412, 7.0, 0.123, -2.0123, 10, 6]
LN= Num.float2num_list(L)
print(list(i.n for i in LN)) #['1011.0', '0.0', '9.998412', '7.0', '0.123', '-2.0123', '10.0', '6.0']
print(list(i for i in LN)) #[Num('1011.0'), Num('0.0'), Num('9.998412'), Num('7.0'), Num('0.123'), Num('-2.0123'), Num('10.0'), Num('6.0')]SAVE NUMERIC LIST TO DISK FILE >>>
Num.f_filewrite(L) #
READ NUMERIC LIST FROM DISK FILE (nums.txt default filename) >>>
L = Num.f_fileread(); print(L) #[Num('1011.0'), Num('0.0'), Num('9.998412'), Num('7.0'), Num('0.123'), Num('-2.0123'), Num('10.0'), Num('6.0')]
### FAQ
Q. I usually try to add 0.1 to 0.2 in python3 with this code:
>>> print(0.1 + 0.2)
and the result is:>>> 0.30000000000000004
How instead can it gets exactly 0.3?
A. Using Num class >>>from num7 import Num, Num as calc
print(Num('0.1') + Num('0.2')) #calc.add('0.1', '0.2') #0.3Q. I'll get an error when i usually type:
>>> Num(0.1)
Traceback (most recent call last):
File "", line 1, in
File "C:\Users\pincopallino\mydata\Python\Python310\lib\site-packages\num7.py", line 470, in __init__
raise ValueError(F"Num.__init__ => float, type not valid: {n}")
ValueError: Num.__init__ => float, type not valid: 1.0
What is wrong?
A. You must use quotes or string conversion with built-in str function:>>> from num7 import Num
>>> Num('0.1') #Num('0.1')
>>> Num(str(0.1)) #Num('0.1')Q. How can i convert a regular float to a Decimal?
A. With Num.ieee754() method >>>from num7 import Num, Num as calc
a=0.1; b=0.2;
c=a+b #0.30000000000000004 => PRECISION FAILURE!
an = Num.ieee754(a); print(an) #0.1000000000000000055511151231257827021181583404541015625
bn = Num.ieee754(b); print(bn) #0.200000000000000011102230246251565404236316680908203125
cn = Num.ieee754(a+b);
print(cn, '=> PRECISION FAILURE!') #0.3000000000000000444089209850062616169452667236328125 => PRECISION FAILURE!
T = calc.add(an, bn)
print(T, '=> OK.') #0.3000000000000000166533453693773481063544750213623046875 => OK.Q. I have two float variables in my code:
>>> a = 0.1; b = 0.2
How can i convert them in Num type?
A. With Num.float2num method (or directly with str() built-in function) >>>from num7 import Num
a = 0.1; b = 0.2 #
an= Num.float2num(a); bn= Num.float2num(b) #an= Num(str(a)); bn= Num(str(b))
print(an+bn, 'OK. VS', a+b, 'PRECISION FAILURE!') #0.3 OK. VS 0.30000000000000004 PRECISION FAILURE!Q. Can i do add or other math operations also with 10,000 digits after floating point?
A. Yes, you can. >>>from num7 import Num
print((Num('1.123456789e-10_000') + Num('3.987654321e-10_000')).num2exp()) #5.11111111e-10000
print((Num('1.123456789e-10_000') - Num('3.987654321e-10_000')).num2exp()) #-2.864197532e-10000
print((Num('1.123456789e-10_000') * Num('3.987654321e-10_000')).num2exp()) #4.479957319112635269e-20000
print((Num('1.123456789e-10_000') / Num('3.987654321e-10_000'))) #0.28173374584742497292307298769992856660154820877213142969420392746224704666420356
Q. With Python 3.11 it gets an error when running code with digits thousands >>>from num7 import Num
print((Num('1.123456789e-10_000') + Num('3.987654321e-10_000')).num2exp()) #5.11111111e-10000
ValueError: Exceeds the limit (4300) for integer string conversion: value has 10010 digits; use sys.set_int_max_str_digits() to increase the limit
How can i fix it?
A. Set the max string digits allowed in this way >>>from num7 import Num
import sys
sys.set_int_max_str_digits(1_000_000) #1_000_000 str digits set
print((Num('1.123456789e-10_000') + Num('3.987654321e-10_000')).num2exp()) #5.11111111e-10000Q. I must enter many integer variables in my code:
>>> a = Num('123.0'); b = Num('456.0'); c = Num('789.0')
Can i input them without quotes and suffix .0?
A. Yes, this the way:>>> a = Num(123); b = Num(456); c = Num(789)