Python Concepts/Numbers
Objective
[edit | edit source]
|
Lesson
[edit | edit source]
Data Types[edit | edit source]This is the first of several lessons on the data types used by Python. Computer programs can process instructions that work with many different kinds of data, and the instructions need to be very precise. If you add a word to this sentence, 'add' means something very different from when you add 2 and 3. A computer language has to have a set of rules defining what operations can be applied to different kinds of data, and for this to work, there also has to be a set of rules defining exactly what data can be used with each operation. For example, if you want to calculate grossProfit = salesIncome - costs, a program has to know that these quantities are variables containing numbers rather than just strings of letters. They must have a numerical data type rather than string data type. If you are not clear about the meaning in computer science of variables and of data types, it may help to brush up on the lesson Introduction_to_Programming/Variables. Two useful Built-in Functions[edit | edit source]class type(object)[edit | edit source]With one argument, return the type of an object. >>> type(6)
<class 'int'>
>>>
>>> type(6.4)
<class 'float'>
>>>
>>> type('6.4')
<class 'str'>
>>>
>>> type(b'6.4')
<class 'bytes'>
>>>
>>> type(['6.4'])
<class 'list'>
>>>
isinstance(object, classinfo)[edit | edit source]Return true if the object argument is an instance of the classinfo argument. classinfo can be a tuple. isinstance() arg 2 must be a type or tuple of types. >>> isinstance(6,int)
True
>>>
>>> isinstance(6,str)
False
>>>
>>> isinstance('6',str)
True
>>>
>>> isinstance('6',(int,float,bytes))
False
>>>
>>> isinstance('6',(int,float,bytes,str))
True
>>>
>>> isinstance({},dict)
True
>>>
>>> isinstance({3,4,5},set)
True
>>>
>>> isinstance(b'',str)
False
>>>
>>> isinstance(b'',bytes)
True
>>>
|
Python Integers
[edit | edit source]
Introduction to integers[edit | edit source]Python has several data types to represent numbers. This lesson introduces two: integers, and floating point numbers, or 'floats'. We'll discuss floats later in the lesson. An integer, commonly abbreviated to int, is a whole number (positive, negative, or zero). So >>> isinstance(7, int)
True
>>> isinstance(0, int)
True
>>> isinstance(-11, int)
True
>>> isinstance(2, int)
True
>>> isinstance(5, int)
True
>>> isinstance(3.14159, int)
False
>>> isinstance(0.0001, int)
False
>>> isinstance(11.11111, int)
False
>>> isinstance(2.0, int)
False
>>> 2+2
4
>>> 4-2
2
>>> 6+1
7
>>> 6+7-3
10
>>> 2*2
4
>>> 2*2*2
8
>>> -2
-2
>>> 8/2
4.0
>>> 4*4/2
8.0
>>> 4-4*2
-4
>>> 2-4
-2
>>> 10+10/2
15.0
You can do more mathematical operations than the previously demonstrated ones. We can perform a floor division by using two forward slashes ( >>> 4 // 2
2
>>> 1 // 8
0
>>> 5 // 5
1
>>> 100 // 5
20
>>> 4 // 3
1
>>> 5 % 4
1
>>> 1 % 4
1
>>> 4 % 4
0
>>> 2 % 4
2
>>> 2 % 1
0
>>> 20 % 2
0
>>> 20 % 3
2
>>> -20 % 3
1
The >>> divmod(7,3)
(2, 1)
>>> (q,r) = divmod(7,3)
>>> q; r
2
1
You can also find the power of a number by using two asterisk symbols ( >>> 4 ** 2
16
>>> 4 ** 4
256
>>> 1 ** 11278923689
1
>>> 2 ** 4
16
>>> 10 ** 2
100
>>> 1024 ** 2
1048576
>>> 10 ** 6
1000000
>>> 25 ** (-1/2)
0.2
>>> 4 * - 3 ** 2
-36
>>> 4 * (- 3) ** 2
36
>>> 8 / 4 ** 2
0.5
The operator of exponentiation If unsure of precedence, you can always use parentheses to force the desired result: >>> (4 * (- 3)) ** 2 ; 4 * ((- 3) ** 2)
144
36
>>>
There is no limit for the length of integer literals apart from what can be stored in available memory. Non-decimal Integers[edit | edit source]Almost everyone is familiar with ten based numbers. While base 10 is useful for everyday tasks, it isn't ideal for use in the computer world. Three other numeral systems are commonly used in computer science; binary, octal, and hexadecimal. We'll lightly cover Python's use of these in this section. The binary system is essential as all information is represented in binary form in computer hardware. Octal and hexadecimal are convenient for condensing binary numbers to a form that is more easily read by humans, while (unlike decimal) being simple to translate to or from binary. If you have difficulty with this part of the lesson, it may help to brush up on the lesson Numeral_systems in the course Introduction_to_Computers. Most people have heard of binary and it is often associated with computers. Actually, modern binary made its way into the world far before electricity was widely in use. The binary system is 2 based, which means that only two numbers are used. Of course, these numbers are 0 and 1. So unlike the decimal's To use binary numbers in python, prepend >>> 0B11
3
>>> 0B1 + 0B1
2
>>> 0B11 + 0B1
4
>>> 0B10001 + 0B1
18
>>> 0B10001 - 0B1
16
>>> bin(2345)
'0b100100101001'
>>> 0b_111_0101_0011
1875
The value returned by
>>> 0o3
3
>>> 0o12
10
>>> 0o12 + 0o10
18
>>> 0o12 - 0o03
7
>>> 0o100
64
>>> 0o777
511
>>> 0o777 - 0o111
438
>>> oct(1_234_987)
'0o4554053'
>>> 0o_1234_9876
File "<stdin>", line 1
0o_1234_9876
^
SyntaxError: invalid token
>>> 0o_1234_0765
2736629
>>> 0xF
15
>>> 0xF0
240
>>> 0xFF - 0xF
240
>>> 0xF + 0xA
25
>>> 0x2 + 0x2
4
>>> 0x12 - 0xA
8
>>> 0xFF / 0xF
17.0
>>> 0xF * 0xF
225
>>> hex(1_234_987)
'0x12d82b'
>>> 0x_12_D82B
1234987
Bitwise Operators[edit | edit source]All integers may be tested or modified by the Bitwise Operators: These operators are called 'bitwise' because they operate on individual bits within the integer.
>>> bin (0b1010101 & 0b1111)
'0b101'
>>> bin (0b1010101 & 0b111000)
'0b10000'
>>> hex (0xFF00FF & 0xFF00)
'0x0'
In the first example both input operands 0b1010101
0b 1111
^ ^
'0b101'.
>>> bin (0b1010101 | 0b1110)
'0b1011111'
>>> bin (0b1010101 | 0b1100)
'0b1011101'
>>> hex (0xFF00FF | 0x3F0)
'0xff03ff'
In the first example both input operands 0b1010101
0b 1110
^ ^^^^^
'0b1011111'.
>>> bin (0b1010101 ^ 0b1110)
'0b1011011'
>>> bin (0b1010101 ^ 0b1100)
'0b1011001'
>>> hex (0xFF00FF ^ 0x3F0)
'0xff030f'
In the first example both input operands 0b1010101
0b 1110
^ ^^ ^^
'0b1011011'.
>>> bin(0b10101 << 2)
'0b1010100'
>>> bin(0b10101 << 5)
'0b1010100000'
>>> hex(0xFF00FF << 8)
'0xff00ff00'
>>> (0xFF00FF << 8) == (0xFF00FF * 2**8)
True
In the first example the output is the input shifted left 2 bits: 0b 10101
0b1010100
^^
>>> bin(0b10101 >> 2)
'0b101'
>>> bin(0b10101 >> 5)
'0b0'
>>> hex(0xFF00FF >> 8)
'0xff00'
>>> (0xFF00FF >> 8) == (0xFF00FF // 2**8)
True
In the first example the output is the input shifted right 2 bits: 0b10101
0b 101
twoBits = operand & 0x3
The bitwise operators above perform as expected on all integers of (almost) unlimited length: >>> hex( ( 0x1234_FEDC << 120 ) | ( 0x_CDE_90AB << 60 ) )
'0x1234fedc00000000cde90ab000000000000000'
>>> hex( ( 0x1234_FEDC << 200 ) ^ ( 0x_CDE_90AB << 207 ) )
'0x67d7cab5c00000000000000000000000000000000000000000000000000'
6. The behavior of the invert (~) operator shows that negative numbers are treated as their 2’s complement value: >>> a = 0b1100101100101 ; bin(~a)
'-0b1100101100110'
For a true 1's complement bitwise invert here is one way to do it: >>> a = 0b1100101100101 ; b = a ^ ( (1 << a.bit_length()) - 1 ); bin(b)
'0b11010011010'
>>> c = a + b; bin(c)
'0b1111111111111' # to test the operation, all bits of c should be set.
>>> (c+1) == ( 1 << (c.bit_length()) )
True # they are.
And another way to do it: from decimal import *
a = 0b11100100011001110001010111 # a is int
b = bin(a) # b is string
print ('a =', b)
formerPrecision = getcontext().prec
getcontext().prec = a.bit_length()
d = Decimal.logical_invert( Decimal( b[2:] ) ) # d is Decimal object.
getcontext().prec = formerPrecision
print ('d =', d)
e = int(str(d),2) # e is int
print ('e =', bin(e))
( (a + e) == ( ( 1 << a.bit_length() ) - 1 ) ) and print ('successful inversion')
When you execute the above code, you see the following results: a = 0b11100100011001110001010111
d = 11011100110001110101000
e = 0b11011100110001110101000
successful inversion
The Decimal.logical_invert() performs a 1's complement inversion. |
Python Floats
[edit | edit source]Introduction to floats
[edit | edit source]
Although integers are great for many situations, they have a serious limitation, integers are whole numbers. This means that they do not include all real numbers. A real number is a value that represents a quantity along a continuous line[5], which means that it can have fractions in decimal forms. >>> isinstance(4.5, float)
True
>>> isinstance(1.25, float)
True
>>> isinstance(0.75, float)
True
>>> isinstance(3.14159, float)
True
>>> isinstance(2.71828, float)
True
>>> isinstance(1.0, float)
True
>>> isinstance(271828, float)
False
>>> isinstance(0, float)
False
>>> isinstance(0.0, float)
True
The basic arithmetic operations used for integers will also work for floats. (Bitwise operators will not work with floats.) >>> 4.0 + 2.0
6.0
>>> -1.0 + 4.5
3.5
>>> 1.75 - 1.5
0.25
>>> 4.13 - 1.1
3.03
>>> 4.5 // 1.0
4.0
>>> 4.5 / 1.0
4.5
>>> 4.5 % 1.0
0.5
>>> 7.75 * 0.25
1.9375
>>> 0.5 * 0.5
0.25
>>> 1.5 ** 2.0
2.25
|
Some technical information about 'floats.'
[edit | edit source]A floating point literal can be either pointfloat or exponentfloat.
A pointfloat contains a decimal point (".")
and at least one digit ("0"..."9"),
for example:
34.45 ; 34. ; .45 ; 0. ; -.00 ; -33. ;
An exponentfloat contains an exponent which ::= ("e" | "E")["+" | "-"]decinteger.
("e" | "E")
means that "e"
or "E"
is required.
["+" | "-"]
means that "+"
or "-"
is optional.
decinteger
means decimal integer.
These are examples of exponents: e9 ; e-0 ; e+1 ; E2 ; E-3 ; E+4 ;
The exponent is interpreted as follows:
; ; ; ; ;
An exponent float can be either:
decinteger exponent, for example: 0e0 ; -3e1 ; 15E-6 ;
or
pointfloat exponent, for example: .5E+2 ; -3.00e-5 ; 123_456.75E-5 ;
The separate parts of a floating point number are:
1.2345 = 12345e-4 =
[6]
The significand
may be called mantissa
or coefficient.
The base
may be called radix.
The exponent
may be called characteristic
or scale.
Within the floating point literal white space is not permitted.
An underscore ("_")
may be used to improve readability.
Integer and exponent parts are always interpreted using radix 10.
Within the context of floating point literals, a "decinteger" may begin with a "0".
Numeric literals do not include a sign; a phrase like -1
is actually an expression
composed of the unary operator -
and the literal 1.
sys.float_info
[edit | edit source]Object sys.float_info
contains information about floats:
>>> import sys
>>> print ( '\n'.join(str(sys.float_info).split(', ')) )
sys.float_info(max=1.7976931348623157e+308 # maximum representable finite float
max_exp=1024
max_10_exp=308
min=2.2250738585072014e-308 # minimum positive normalized float
min_exp=-1021
min_10_exp=-307
dig=15 # maximum number of decimal digits that can be faithfully represented in a float
mant_dig=53 # float precision: the number of base-radix digits in the significand of a float
epsilon=2.220446049250313e-16
radix=2 # radix of exponent representation
rounds=1)
>>>
Information about some of the above values follows:
sys.float_info.mant_dig
[edit | edit source]>>> sys.float_info.mant_dig
53
>>>
>>> sys.float_info[7]
53
>>>
>>> I1 = (1<<53) - 1 ; I1 ; hex(I1) ; I1.bit_length()
9007199254740991
'0x1fffffffffffff'
53
>>> float(I1-1) ; float(I1-1) == I1-1
9007199254740990.0
True
>>> float(I1) ; float(I1) == I1
9007199254740991.0
True
>>> float(I1+1) ; float(I1+1) == I1+1
9007199254740992.0
True
>>> float(I1+2) ; float(I1+2) == I1+2
9007199254740992.0 # Loss of precision occurs here.
False
>>>
>>> I2 = I1 - 10**11 ; I2 ; hex(I2) ; I2.bit_length() ; float(I2) == I2 ; len(str(I2))
9007099254740991
'0x1fffe8b78917ff'
53
True # I2 can be accurately represented as a float.
16
>>> I3 = I1 + 10**11 ; I3 ; hex(I3) ; I3.bit_length() ; float(I3) == I3 ; len(str(I3))
9007299254740991
'0x2000174876e7ff'
54 # Too many bits.
False # I3 can not be accurately represented as a float.
16
>>>
sys.float_info.dig
[edit | edit source]>>> len(str(I1))
16
>>>
>>> sys.float_info.dig
15
>>> sys.float_info[6]
15
>>>
As shown above some (but not all) decimal numbers of 16 digits can be accurately represented as a float.
Hence 15 as the limit in sys.float_info.dig
.
sys.float_info.max
[edit | edit source]>>> sys.float_info.max
1.7976931348623157e+308
>>>
>>> sys.float_info[0]
1.7976931348623157e+308
>>>
>>> 1.7976931348623157e+305
1.7976931348623156e+305
>>> 1.7976931348623157e+306
1.7976931348623156e+306
>>> 1.7976931348623157e+307
1.7976931348623158e+307
>>> 1.7976931348623157e+308
1.7976931348623157e+308
>>> 1.7976931348623157e+309
inf
>>>
sys.float_info.min
[edit | edit source]>>> sys.float_info.min
2.2250738585072014e-308
>>> sys.float_info[3]
2.2250738585072014e-308
>>>
>>> 2.2250738585072014e-306
2.2250738585072014e-306
>>> 2.2250738585072014e-307
2.2250738585072014e-307
>>> 2.2250738585072014e-308
2.2250738585072014e-308
>>> 2.2250738585072014e-309
2.225073858507203e-309 # Loss of precision.
>>> 2.2250738585072014e-310
2.2250738585072e-310
>>> 2.2250738585072014e-311
2.225073858507e-311
>>>
The Precision of Floats
[edit | edit source]Before you start calculating with floats you should understand that the precision of floats has limits, due to Python and the architecture of a computer. Some examples of errors due to finite precision are displayed below.
>>> 1.13 - 1.1
0.029999999999999805
>>> 0.001 / 11.11
9.000900090009002e-05
>>> 1 + .0000000000000001
1.0
>>> -5.5 % 3.2
0.9000000000000004
>>> float(1_234_567_890_123_456)
1234567890123456.0
>>> float(12_345_678_901_234_567)
1.2345678901234568e+16
In the first example, 1.13 - 1.1 = 0.03
, although Python comes to the conclusion that the real answer is 0.029999999999999805
. The fact behind this reasoning is based on how the computer stores memory, so the difference lost a little of its precision. As the minuend increases in size, so does its precision. 2.13 - 1.1 = 1.0299999999999998
and 3.13 - 1.1 = 2.03
.
In the second example, 0.001 / 11.11 = 9.000900090009002e-05
where e-05
means ten to the power of negative five. The answer could also be 9.000900090009001e-05
depending on how the quotient is rounded, how long the quotient can be stored on the computer, and the most significant number on the right hand side.
In the third example, the sum of the addends 1 + .0000000000000001 = 1.0
although we know that it really is 1 + .0000000000000001 = 1.0000000000000001
. The reason the second addend is left out is because of its insignificance. Although this might not matter for every day situations, it may be important for such uses as rocket science and possibly calculus.
The fourth example gives the correct result if rewritten:
>>> ((-5.5*10 ) % (3.2*10)) / 10.0
0.9
When working with Python floats, we need to be aware that there will probably be a margin of error.
Decimal fixed point and floating point arithmetic for extreme precision
[edit | edit source]The Python "Decimal" module provides support for fast correctly-rounded decimal floating point arithmetic. The module offers several advantages over the float datatype, including:
- Decimal numbers can be represented exactly.
- The decimal module has a user alterable precision (defaulting to 28 places) which can be as large as needed for a given problem.
The usual start to using decimals is importing the module, viewing the current context with getcontext() and, if necessary, setting new values for precision, rounding, or enabled traps:
>>> from decimal import *
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[Inexact, FloatOperation, Rounded], traps=[InvalidOperation, DivisionByZero, Overflow])
>>> setcontext(ExtendedContext)
>>> getcontext()
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[])
>>> setcontext(BasicContext)
>>> getcontext()
Context(prec=9, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[Clamped, InvalidOperation, DivisionByZero, Overflow, Underflow])
>>> c = getcontext()
>>> c.flags[Inexact] = True
>>> c.flags[FloatOperation] = True
>>> c.flags[Rounded] = True
>>> getcontext()
Context(prec=9, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[Inexact, FloatOperation, Rounded], traps=[Clamped, InvalidOperation, DivisionByZero, Overflow, Underflow])
>>> getcontext().prec = 75 # set desired precision
>>> getcontext()
Context(prec=75, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[Inexact, FloatOperation, Rounded], traps=[Clamped, InvalidOperation, DivisionByZero, Overflow, Underflow])
We are now ready to use the decimal module.
>>> Decimal(3.14) # Input to decimal() is float.
Decimal('3.140000000000000124344978758017532527446746826171875') # Exact value of float 3.14.
>>> Decimal('3.14') # Input to decimal() is string.
Decimal('3.14') # Exact value of 3.14 in decimal floating point arithmetic.
>>> (2 ** 0.5)**2
2.0000000000000004 # Result of binary floating point operation. We expect 2.
>>> (Decimal('2') ** Decimal('0.5')) ** Decimal('2')
Decimal('1.99999999999999999999999999999999999999999999999999999999999999999999999999')
# Result of decimal floating point operation with string input. We expect 2.
>>> (2.12345678 ** (1/2.345)) ** 2.345
2.1234567800000006 # Result of floating point operation. We expect 2.12345678.
>>> (Decimal('2.12345678') ** (Decimal('1')/Decimal('2.345'))) ** Decimal('2.345')
Decimal('2.12345677999999999999999999999999999999999999999999999999999999999999999999')
# Result of decimal floating point operation with string input . We expect 2.12345678.
>>> getcontext().rounding=ROUND_UP
>>> (Decimal('2.12345678') ** (Decimal('1')/Decimal('2.345'))) ** Decimal('2.345')
Decimal('2.12345678000000000000000000000000000000000000000000000000000000000000000003')
# Result of decimal floating point operation with string input . We expect 2.12345678.
Some mathematical functions are also available to Decimal:
>>> getcontext().prec = 30
>>> Decimal(2).sqrt()
Decimal('1.41421356237309504880168872421')
>>> (Decimal(2).sqrt())**2
Decimal('2.00000000000000000000000000001') # We expect 2.
>>> Decimal(1).exp()
Decimal('2.71828182845904523536028747135') # Value of 'e', base of natural logs.
>>> Decimal( Decimal(1).exp() ).ln()
Decimal('0.999999999999999999999999999999') # We expect 1.
Lack of precision in the real world
[edit | edit source](included for philosophical interest)
>>> a = 899_999_999_999_999.1 ; a - (a - .1)
0.125
>>> 1.13 - 1.1
0.029999999999999805
Simple tests indicate that the error inherent in floating point operations is about
This raises the question "How much precision do we need?"
For decades high school students calculated sines and cosines to 4 decimal places by referring to printed look-up tables. Before computers engineers used slide rules to make calculations accurate to about
for most calculations, and the Brooklyn Bridge is still in regular use.
With accuracy of engineers can send a rocket to Pluto and miss by 1cm.
If your calculations produce a result of and you were expecting
will you be satisfied with your work? If your calculations were in meters, probably yes. If your calculations were in nanometers ( of a meter), probably no.
Knowing that lack of precision is inherent in floating point operations, you may have to include possibly substantial amounts of code to make allowances for it.
Extreme Precision
[edit | edit source](included for historical interest)
If you must have a result correct to 50 places of decimals, Python's integer math comes to the rescue. Suppose your calculation is:
For 50 significant digits after the decimal point your calculation becomes:
>>> dividend = 12345678900
>>> divisor = 456787654
>>>
>>> (quotient, remainder) = divmod(dividend*(10**51), divisor) ; quotient;remainder
27027172892899596625262555804540198890751981663672547
231665262
>>> if remainder >= ((divisor + (divisor & 1)) >> 1) : quotient += 1
...
>>> quotient
27027172892899596625262555804540198890751981663672548
>>>
The correct result but note:
>>> quotient*(10**(-51))
27.027172892899596 # Lack of precision.
>>>
Put the decimal point in the correct position within a string to preserve precision:
>>> str(quotient)[0:-51] + '.' + str(quotient)[-51:]
'27.027172892899596625262555804540198890751981663672548'
>>>
Format the result:
>>> s1 = str(quotient)[-51:] ; s1
'027172892899596625262555804540198890751981663672548'
>>> L2 = [ s1[p:p+5] for p in range(0,51,5) ] ; L2
['02717', '28928', '99596', '62526', '25558', '04540', '19889', '07519', '81663', '67254', '8']
>>> decimal = '_'.join(L2) ; decimal
'02717_28928_99596_62526_25558_04540_19889_07519_81663_67254_8'
>>> str(quotient)[0:-51] + '.' + decimal
'27.02717_28928_99596_62526_25558_04540_19889_07519_81663_67254_8'
# The result formatted for clarity and accurate to 50 places of decimals.
>>>
Both strings
'27.027172892899596625262555804540198890751981663672548'
and
'27.02717_28928_99596_62526_25558_04540_19889_07519_81663_67254_8'
are
acceptable as input to Python's Decimal module.
Lack of precision and what to do about it
[edit | edit source]Lack of precision in floating point operations quickly becomes apparent:
sum = 0
increment = 0.000_000_000_1
for count in range(1,1000) :
sum += increment
print ('count= {}, sum = {}'.format(count,sum))
if sum != count/10_000_000_000 : break
count= 1, sum = 1e-10 count= 2, sum = 2e-10 count= 3, sum = 3e-10 count= 4, sum = 4e-10 count= 5, sum = 5e-10 count= 6, sum = 6e-10 count= 7, sum = 7e-10 count= 8, sum = 7.999999999999999e-10
The problem seems to be that floating point numbers are contained in 53 bits, limiting the number of significant digits in the decimal number displayed to 15 or 16. But this is not really the problem. If the standard limits the number of significant digits displayed to 15 or 16, so be it. The real problem is that underlying calculations are also performed in 53 bits.
>>> (0.000_000_000_1).hex()
'0x1.b7cdfd9d7bdbbp-34'
>>> h1 = '0x1b7cdfd9d7bdbbp-86' # increment with standard precision.
>>> float.fromhex(h1)
1e-10
>>>
Precision greater than standard
[edit | edit source]
Rewrite the above code so that the value
>>> x,r = divmod (16**26,10**10) ;x;r
2028240960365167042394
7251286016
>>> x += (r >= (10**10)/2);x
2028240960365167042395
>>> h1 = hex(x)[2:].upper();h1
'6DF37F675EF6EADF5B'
>>> increment = '0x' + h1 + 'p-104' ; increment
'0x6DF37F675EF6EADF5Bp-104' # Current value of increment.
>>> int(increment[:-5],16).bit_length()
71 # Greater precision than standard by 18 bits.
>>> float.fromhex(increment)
1e-10
>>>
Exact value of increment: >>> from decimal import *
>>> Decimal(x) / Decimal(16**26)
Decimal('1.0000_0000_0000_0000_0000_01355220626007433600102690740628157139990861423939350061118602752685546875E-10')
>>> # 22 significant digits.
sum = '0x0p0'
for count in range(1,1000) :
hex_val = sum.partition('p')[0]
sum = hex( eval(hex_val) + x ) + 'p-104'
f1 = float.fromhex(sum)
print (
'count = {}, sum = {}, sum as float = {}'.format(count, sum, f1)
)
if f1 != count/10_000_000_000 : exit(99)
count = 1, sum = 0x6df37f675ef6eadf5bp-104, sum as float = 1e-10 count = 2, sum = 0xdbe6fecebdedd5beb6p-104, sum as float = 2e-10 count = 3, sum = 0x149da7e361ce4c09e11p-104, sum as float = 3e-10 ........................... count = 997, sum = 0x1ac354f2d94d7a0b7dd67p-104, sum as float = 9.97e-08 count = 998, sum = 0x1aca342acfc3697a2bcc2p-104, sum as float = 9.98e-08 count = 999, sum = 0x1ad11362c63958e8d9c1dp-104, sum as float = 9.99e-08 Consider the last line above: The most accurate hex representation of value drift vvv count = 999, sum = 0x1ad11362c63958e8d9c1dp-104, sum as float = 9.99e-08 0x1AD11362C63958E8D9B0Ap-104 most accurate hex representation of sum as float ^^^^^^^^^^^^^^^^^^ 18 hex digits = 69 bits drift vvvvv count = 99999, sum = 0xa7c53e539be0252c255b85p-104, sum as float = 9.9999e-06 0xA7C53E539BE0252C24F026p-104 most accurate hex representation of sum as float ^^^^^^^^^^^^^^^^^ 17 hex digits = 68 bits drift vvvvvvvv count = 9999999999, sum = 0xffffffff920c8098a1aceb2ca5p-104, sum as float = 0.9999999999 0xFFFFFFFF920C8098A1091520A5p-104 most accurate hex representation of sum as float ^^^^^^^^^^^^^^^^^^ 18 hex digits = 72 bits drift 15 decimal digits vvvvvvvvvvvvv count = 999999999999999, sum = 0x1869fffffffff920c81929f8524a0a5p-104, sum as float = 99999.9999999999 0x1869FFFFFFFFF920C8098A1091520A5p-104 most accurate hex representation of sum as float ^^^^^^^^^^^^^^^^^^ 18 hex digits = 69 bits
While floating point operations implemented in software might not depend on conversion to and from hex strings, the above illustrates the accuracy that could be obtained if floating point software of selectable precision were to replace now antiquated floating point hardware.
>>> 1.13 - 1.1
0.029999999999999805
>>>
In a programming language as magnificent as Python, the above result is intolerable. |
Python's Decimal module
[edit | edit source]
With a few simple changes the above counting loop takes full advantage of Python's Decimal module, and possible loss of precision becomes irrelevant. from decimal import *
def D(v1) : return Decimal(str(v1))
sum = 0
increment = D(0.000_000_000_1)
for count in range(1,1000) :
sum += increment
print ('count = {}, sum = {}'.format(count,sum))
if sum != count * increment :
exit (99)
exit (0)
count = 1, sum = 1E-10 count = 2, sum = 2E-10 count = 3, sum = 3E-10 ................. count = 997, sum = 9.97E-8 count = 998, sum = 9.98E-8 count = 999, sum = 9.99E-8 A float is displayed with 'e', a Decimal object with 'E'. >>> 9.99E-8
9.99e-08
>>> Decimal(str(9.99e-8))
Decimal('9.99E-8')
>>>
|
Reset the float
[edit | edit source]Using formatted string
[edit | edit source]
sum = 0
increment = 0.000_000_000_1
for count in range(1,1000) :
sum += increment
s1 = '{0:.10f}'.format(sum)
sum = float(s1)
print ('count= {}, sum = {}'.format(count,sum))
if sum != count / 10_000_000_000 :
exit (99)
exit (0)
count= 1, sum = 1e-10 count= 2, sum = 2e-10 count= 3, sum = 3e-10 ................. count= 997, sum = 9.97e-08 count= 998, sum = 9.98e-08 count= 999, sum = 9.99e-08
|
Using Decimal precision
[edit | edit source]
Python's floating point standard states that the best accuracy to be expected is 15 significant decimal digits. from decimal import *
getcontext().prec = 15
sum = 0
increment = 0.000_000_000_1
for count in range(1,1000) :
print ( 'count =', (' '+str(count))[-3:], end=' ' )
sum += increment
d1 = Decimal(str(sum))
print ( 'd1 = sum =', (' ' + str(d1))[-21:], end=' ' )
d1 += 0 # This forces d1 to conform to 15 digits of precision.
print ( 'd1 =', (' ' + str(d1))[-20:], end=' ' )
sum = float(d1)
print ('sum =', sum)
if sum != count / 10_000_000_000 :
print (' ', sum, count, increment, count*increment)
exit (99)
exit (0)
count = 1 d1 = sum = 1E-10 d1 = 1E-10 sum = 1e-10 count = 2 d1 = sum = 2E-10 d1 = 2E-10 sum = 2e-10 count = 3 d1 = sum = 3E-10 d1 = 3E-10 sum = 3e-10 count = 4 d1 = sum = 4E-10 d1 = 4E-10 sum = 4e-10 count = 5 d1 = sum = 5E-10 d1 = 5E-10 sum = 5e-10 count = 6 d1 = sum = 6E-10 d1 = 6E-10 sum = 6e-10 count = 7 d1 = sum = 7E-10 d1 = 7E-10 sum = 7e-10 count = 8 d1 = sum = 7.999999999999999E-10 d1 = 8.00000000000000E-10 sum = 8e-10 count = 9 d1 = sum = 9E-10 d1 = 9E-10 sum = 9e-10 count = 10 d1 = sum = 1E-9 d1 = 1E-9 sum = 1e-09 count = 11 d1 = sum = 1.1000000000000001E-9 d1 = 1.10000000000000E-9 sum = 1.1e-09 count = 12 d1 = sum = 1.2E-9 d1 = 1.2E-9 sum = 1.2e-09 count = 13 d1 = sum = 1.3E-9 d1 = 1.3E-9 sum = 1.3e-09 count = 14 d1 = sum = 1.4000000000000001E-9 d1 = 1.40000000000000E-9 sum = 1.4e-09 count = 15 d1 = sum = 1.5E-9 d1 = 1.5E-9 sum = 1.5e-09 count = 16 d1 = sum = 1.6E-9 d1 = 1.6E-9 sum = 1.6e-09 .............................. count = 296 d1 = sum = 2.96E-8 d1 = 2.96E-8 sum = 2.96e-08 count = 297 d1 = sum = 2.97E-8 d1 = 2.97E-8 sum = 2.97e-08 count = 298 d1 = sum = 2.9800000000000002E-8 d1 = 2.98000000000000E-8 sum = 2.98e-08 count = 299 d1 = sum = 2.9899999999999996E-8 d1 = 2.99000000000000E-8 sum = 2.99e-08 count = 300 d1 = sum = 3.0000000000000004E-8 d1 = 3.00000000000000E-8 sum = 3e-08 count = 301 d1 = sum = 3.01E-8 d1 = 3.01E-8 sum = 3.01e-08 count = 302 d1 = sum = 3.02E-8 d1 = 3.02E-8 sum = 3.02e-08 .............................. count = 997 d1 = sum = 9.97E-8 d1 = 9.97E-8 sum = 9.97e-08 count = 998 d1 = sum = 9.98E-8 d1 = 9.98E-8 sum = 9.98e-08 count = 999 d1 = sum = 9.989999999999999E-8 d1 = 9.99000000000000E-8 sum = 9.99e-08 The last line: count = 999 d1 = sum = E-8 d1 = E-8 sum = 9.99e-08 The value |
The Boolean
[edit | edit source]
In Python and most languages, a Boolean can be either >>> 1 == 1
True
>>> 1 == 0
False
>>> bool(0)
False
>>> bool(1)
True
>>> bool(10001219830)
True
>>> bool(-1908)
True
>>> bool("Hello!")
True
>>> bool("")
False
>>> bool(" ")
True
>>> bool(None)
False
>>> bool(0.000000000000000000000000000000000)
False
>>> bool("0.000000000000000000000000000000000")
True
>>> bool(0.0)
False
>>> bool([])
False
>>> bool([1, 2, 3])
True
>>> bool()
False
>>> bool(True)
True
>>> bool(False)
False
>>> bool(1==1)
True
>>> bool(1==0)
False
You can also use three operators to alter a Boolean statement[8]: >>> not False
True
>>> not True
False
>>> True and True
True
>>> True and False
False
>>> True or False
True
>>> False or False
False
>>> not(False or False)
True
>>> not(False and False)
True
>>> not(False and True)
True
All of the possible combinations are: True and True: True
True and False: False
False and True: False
False and False: False
True or True: True
True or False: True
False or True: True
False or False: False
(not(True and True)) == ((not True) or (not True)): True
(not(True and False)) == ((not True) or (not False)): True
(not(False and True)) == ((not False) or (not True)): True
(not(False and False)) == ((not False) or (not False)): True
(not(True or True)) == ((not True) and (not True)): True
(not(True or False)) == ((not True) and (not False)): True
(not(False or True)) == ((not False) and (not True)): True
(not(False or False)) == ((not False) and (not False)): True
The above negated statements reflect "De Morgan's laws." For example, the statement (not(True and True)) == ((not True) or (not True)): True
is equivalent to: True and True True or True. A simple way to choose one of two possible values:[edit | edit source]>>> L1 = [1,2,0,3,0,5]
Produce list L2, a copy of L1, except that each value 0 in L1 has been replaced by 0xFF: >>> L2 = []
>>>
>>> for p in L1 :
... L2 += ([p], [0xFF])[p == 0]
...
>>> L2
[1, 2, 255, 3, 255, 5]
>>>
Expressions containing multiple booleans[edit | edit source]Consider the expression: A and B or C
Does this mean (A and B) or C
Does it mean A and (B or C)
It might be tempting to say that there is no difference, but look closely: for A in True, False :
for B in True, False :
for C in True, False :
b1 = (A and B) or C
b2 = A and (B or C)
if b1 != b2 :
print (
'''
for A = {}, B = {}, C = {}
(A and B) or C = {}
A and (B or C) = {}
'''.format(A, B, C, b1, b2)
)
for A = False, B = True, C = True (A and B) or C = True A and (B or C) = False for A = False, B = False, C = True (A and B) or C = True A and (B or C) = False Add another boolean to the expression: A and B or C and D
and the number of different possibilities is at least 96. You can see that the complexity of these expressions quickly becomes unmanageable. The essence of this section: Keep your expressions simple and use parentheses as necessary to ensure that your code is interpreted exactly as you expect.
|
Complex Numbers
[edit | edit source]
A complex number is represented as >>> 1+2j
(1+2j)
>>> -1+5.5j
(-1+5.5j)
>>> 0+5.5j
5.5j
>>> 2j
2j
>>> 1+0j
(1+0j)
>>> complex(3,-2)
(3-2j)
Note also that j cannot be used on its own without b. If you try to use j on its own, Python will look for a variable >>> a = 5 + 3j
>>> a - j
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'j' is not defined
>>> a - 1j
(5+2j)
>>> j = -3j
>>> a - j
(5+6j)
>>> a - 1j
(5+2j)
The last result illustrates that even when the variable j has a numerical value, 1j (where, as above, can be any number) is always interpreted as the imaginary number j, not the variable j.
>>> (1+3j)+(2-5j)
(3-2j)
>>> (1+3j)-(2-5j)
(-1+8j)
>>> (1+3j)*(2-5j)
(17+1j)
>>> a = complex(3,-5) ; b = 1 ; b += 2j ; a ; b
(3-5j)
(1+2j)
>>> a + b ; a - b
(4-3j)
(2-7j)
>>> a * b ; a / b
(13+1j)
(-1.4-2.2j)
>>> a + 4 ; b - 2j ; a * 3.1 ; b / 2
(7-5j)
(1+0j)
(9.3-15.5j)
(0.5+1j)
>>> b ; b /= 5 ; b
(1+2j)
(0.2+0.4j)
>>> a = complex(3,-5j) ; a
(8-0j)
Look closely at the last example. It does not produce an error, but is it what you want?
>>> (1+2j).real
1.0
>>> (1+2j).imag
2.0
>>> var = 5+3j
>>> var.real
5.0
>>> var.imag
3.0
cmath — Mathematical functions for complex numbers[edit | edit source]
Introduction[edit | edit source]
Polar coordinates[edit | edit source]Polar coordinates provide an alternative way to represent a complex number. In polar coordinates, a complex number
>>> tan_phi = Z.imag/Z.real ; tan_phi
0.75
>>> φ = cmath.atan(tan_phi).real ; φ
0.6435011087932844 # φ in radians
>>> φ * (180/π)
36.86989764584402 # φ in degrees.
>>>
Class method
>>> φ1 = cmath.phase(Z) ; φ1
0.6435011087932844
>>> φ1 == φ
True
>>>
From figure 1:
>>> Z ; r ; φ
(3.6+2.7j)
4.5
0.6435011087932844
>>>
>>> cosφ = Z.real / r ; cosφ
0.8
>>> sinφ = Z.imag / r ; sinφ
0.6
>>>
>>> Z1 = r*( cosφ + 1j*sinφ ) ; Z1
(3.6+2.7j)
>>>
De Moivre's formula[edit | edit source]The format containing polar coordinates is useful because:
Z^2[edit | edit source]
\sqrt{Z}[edit | edit source]
>>> sinφ_2 = cmath.sin(φ/2).real ; sinφ_2
0.31622776601683794
>>> cosφ_2 = cmath.cos(φ/2).real ; cosφ_2
0.9486832980505138
>>> Z
(3.6+2.7j)
>>> cmath.sqrt(Z)
(2.0124611797498106+0.670820393249937j)
>>> (r**0.5)*(cosφ_2 + 1J*sinφ_2)
(2.0124611797498106+0.6708203932499369j)
>>>
\sqrt{1}[edit | edit source]
\sqrt{-1}[edit | edit source]
Cube roots of 1 simplified[edit | edit source]
Multiplication of complex numbers[edit | edit source]
Classification functions[edit | edit source]Return True if the values a and b are close to each other and False otherwise. Whether or not two values are considered close is determined according to given absolute and relative tolerances. >>> v1; cmath.polar(v1)
(87283949+87283949j)
(123438144.45328155, 0.7853981633974483)
>>> v2; cmath.polar(v2)
(87283949+87283950j)
(123438145.16038834, 0.7853981691258783)
>>> cmath.isclose(v1,v2)
False
>>>
>>> cmath.isclose(v1,v2, rel_tol=8e-9)
False
>>> cmath.isclose(v1,v2, rel_tol=9e-9)
True
>>>
>>> cmath.isclose(v1,v2, abs_tol=1)
True
>>> cmath.isclose(v1,v2, abs_tol=.5)
False
>>>
The following python code implements this functionality with a list or tuple of numbers as input.
Power and logarithmic functions[edit | edit source]
|
Number Conversions
[edit | edit source]
Introduction[edit | edit source]
Converting integers, decimal to non-decimal[edit | edit source]
Converting integers, non-decimal to decimal[edit | edit source]
Interfacing with Python's Decimal module[edit | edit source]
Converting [edit | edit source] |
Method length (in bytes) must be sufficient to contain int, at least (int.bit_length() + 7) // 8 byteorder can be 'big', 'little' or sys.byteorder, signed must be True if int is negative. For example: >>> int1 = 0x1205
>>> bytes1 = int1.to_bytes(2, byteorder='big') ; bytes1
b'\x12\x05' # A bytes object containing int1.
>>> isinstance(bytes1, bytes)
True
>>>
>>> int2 = 0xe205
>>> bytes2 = int2.to_bytes(2, byteorder='big', signed=True) ; bytes2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: int too big to convert
>>>
>>> bytes2 = int2.to_bytes(3, byteorder='big', signed=True) ; bytes2
b'\x00\xe2\x05'
>>>
>>> bytes2 = int2.to_bytes(2, byteorder='big') ; bytes2
b'\xe2\x05'
>>>
>>> int3 = -7675
>>> bytes3 = int3.to_bytes(2, byteorder='big') ; bytes3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: can't convert negative int to unsigned
>>>
>>> bytes3 = int3.to_bytes(2, byteorder='big', signed=True) ; bytes3
b'\xe2\x05'
>>>
>>> bytes2 == bytes3
True
>>>
The bytes object To preserve the original int, let >>> hex(int2); hex(int3)
'0xe205'
'-0x1dfb'
>>> bytes2 = int2.to_bytes(3, byteorder='big', signed=True) ; bytes2
b'\x00\xe2\x05' # Most significant bit (value=0) preserves sign (+).
>>> bytes3 = int3.to_bytes(2, byteorder='big', signed=True) ; bytes3
b'\xe2\x05' # Most significant bit (value=1) preserves sign (-).
>>>
|
Converting bytes
to int
[edit | edit source]
A bytes object is an immutable sequence with every member an int satisfying 0xFF The classmethod The value returned is an int represented by the given bytes object or any sequence convertible to bytes object: >>> hex(int.from_bytes(b'\xcd\x34', byteorder='little'))
'0x34cd'
>>> hex(int.from_bytes(b'\xcd\x34', byteorder='big'))
'0xcd34'
>>> hex(int.from_bytes(b'\xcd\x34', byteorder='little', signed=True))
'0x34cd'
>>> hex(int.from_bytes(b'\xcd\x34', byteorder='big', signed=True))
'-0x32cc'
>>> hex(int.from_bytes([0xCD,0x34], byteorder='big')) # Input is list convertible to bytes.
'0xcd34'
>>> hex(int.from_bytes((0xCD,0x34), byteorder='big')) # Input is tuple convertible to bytes.
'0xcd34'
>>> hex(int.from_bytes({0xCD,0x34}, byteorder='big'))
'0x34cd' # Ordering of set is unpredictable.
>>> hex(int.from_bytes(bytes([0xCD,0x34]), byteorder='big')) # Input is bytes object.
'0xcd34'
>>> hex(int.from_bytes(bytearray([0xCD,0x34]), byteorder='big')) # Input is bytearray.
'0xcd34'
>>>
Complete conversion[edit | edit source]Complete conversion means conversion from int to bytes to int, or from bytes to int to bytes. When converting int/bytes/int, it is reasonable to expect that the final int should equal the original int. If you keep byteorder consistent and signed=True, you will produce consistent results: Positive number with msb (most significant bit) clear: >>> int1 = 0x1205
>>> bytes1 = int1.to_bytes(2, byteorder='big', signed=True) ; bytes1
b'\x12\x05'
>>> int1a = int.from_bytes(bytes1, byteorder='big', signed=True) ; hex(int1a)
'0x1205'
>>> int1==int1a
True
Negative number with msb clear: >>> int1 = -0x1205
>>> bytes1 = int1.to_bytes(2, byteorder='big', signed=True) ; bytes1
b'\xed\xfb'
>>> int1a = int.from_bytes(bytes1, byteorder='big', signed=True) ; hex(int1a)
'-0x1205'
>>> int1==int1a
True
Positive number with msb set: >>> int1 = 0xF205
>>> bytes1 = int1.to_bytes(2, byteorder='big', signed=True) ; bytes1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: int too big to convert
>>> bytes1 = int1.to_bytes(3, byteorder='big', signed=True) ; bytes1
b'\x00\xf2\x05'
>>> int1a = int.from_bytes(bytes1, byteorder='big', signed=True) ; hex(int1a)
'0xf205'
>>> int1==int1a
True
Negative number with msb set: >>> int1 = -0xF305
>>> bytes1 = int1.to_bytes(2, byteorder='big', signed=True) ; bytes1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: int too big to convert
>>> bytes1 = int1.to_bytes(3, byteorder='big', signed=True) ; bytes1
b'\xff\x0c\xfb'
>>> int1a = int.from_bytes(bytes1, byteorder='big', signed=True) ; hex(int1a)
'-0xf305'
>>> int1==int1a
True
|
floats
[edit | edit source]
Two methods support conversion to and from hexadecimal strings. Because Python’s floats are stored internally as binary numbers, converting a float to or from a decimal string usually involves a small rounding error. In contrast, hexadecimal strings allow exact representation and specification of floating-point numbers. from hex[edit | edit source]
to hex[edit | edit source]
Floating point calculation of [edit | edit source] |
>>> (1.1).hex()
'0x1.199999999999ap+0'
>>> v1 = '0x1199999999999Ap-52' ; float.fromhex(v1)
1.1
>>>
>>> (1.13).hex()
'0x1.2147ae147ae14p+0'
>>> v2 = '0x12147AE147AE14p-52' ; float.fromhex(v2)
1.13
>>>
Difference >>> float.fromhex('0x7AE147AE147Ap-52')
0.029999999999999805
>>> 1.13-1.1
0.029999999999999805
>>>
Exact value of v1: >>> d1 = Decimal(eval(v1[:-4])) / Decimal(2**52) ; d1 ; d1 == Decimal(1.1)
Decimal('1.100000000000000088817841970012523233890533447265625') # > 1.1
>>> True
>>>
Exact value of v2: >>> d2 = Decimal(eval(v2[:-4])) / Decimal(2**52) ; d2 ; d2 == Decimal(1.13)
Decimal('1.12999999999999989341858963598497211933135986328125') # < 1.13
>>> True
>>>
Exact value of difference: >>> d1a = d2-d1 ; d1a
Decimal('0.029999999999999804600747665972448885440826416015625')
>>>
>>> d1a == Decimal(1.13 - 1.1)
>>> True
>>> float(d1a)
0.029999999999999805
>>>
Why the error appears to be so great[edit | edit source]>>> error2 = d2-Decimal('1.13'); error2
Decimal('-1.0658141036401502788066864013671875E-16') # Negative number.
>>>
>>> error1 = d1-Decimal('1.1'); error1
Decimal('8.8817841970012523233890533447265625E-17') # Positive number.
>>>
>>> total_error = error2-error1 ; total_error
Decimal('-1.95399252334027551114559173583984375E-16') # Relatively large negative number.
>>>
>>> total_error + ( Decimal('1.13')-Decimal('1.1') - Decimal(1.13-1.1) )
Decimal('0E-51')
>>>
An observation[edit | edit source]While the value >>> diff1 = '0x7AE14_7AE14_7AE14_7AE14_7AE14p-104'
>>> float.fromhex( diff1.replace('_', '') )
0.03
>>>
Exact decimal value of >>> diff2 = Decimal(eval(diff1[:-5])) / Decimal(2**104) ; diff2
Decimal('0.029999999999999999999999999999976334172843369645837648143041516413109803806946729309856891632080078125')
>>> float(diff2)
0.03
>>>
Should the point in the hex representation be called a "heximal" point? User-written floating point software for an accurate result[edit | edit source]>>> (1.13).hex()
'0x1.2_147ae_147ae_14_p+0' # 13 hexadecimal digits after the hexadecimal point.
>>> (1.1).hex()
'0x1.1_99999_99999_9a_p+0' # 13 hexadecimal digits after the hexadecimal point.
>>>
By inspection produce hex values for >>> v1 = '0x12_147ae_147ae_148_p-56'
# '0x1.2_147ae_147ae_147ae_147ae_147ae_p+0' accurate to 14 hexadecimal digits after the hexadecimal point.
>>> float.fromhex(v1.replace('_',''))
1.13
>>>
>>> v2 = '0x1_19999_99999_999a_p-56' # Accurate to 14 hexadecimal digits after the hexadecimal point.
>>> float.fromhex(v2.replace('_',''))
1.1
>>>
Slightly more precision in the underlying calculations produces an accurate difference: >>> diff = hex(eval(v1[:-5]) - eval(v2[:-5])) + 'p-56' ; diff
'0x7ae147ae147aep-56'
>>> float.fromhex(diff)
0.03
>>>
Exact value of >>> from decimal import *
>>> getcontext().prec = 100
>>>
>>> d1 = Decimal( eval(diff[:-4]) ) / Decimal(2**56) ; d1
Decimal('0.0299999999999999988897769753748434595763683319091796875')
>>> float(d1)
0.03
>>>
>>> getcontext().prec = 15 # Maximum precision of floats.
>>> d1 += 0 ; d1
Decimal('0.0300000000000000') # Accurate to 15 digits of precision.
>>> float(d1)
0.03
>>>
Summary: >>> import sys
>>> sys.float_info.mant_dig
53
>>> sys.float_info.mant_dig = 57
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: readonly attribute
>>>
This section simulates the floating-point calculation of if the value of attribute
For examples of professionally written code that temporarily increases precision for underlying calculations and then restores precision to display result, see recipes. |
Miscellaneous Topics
[edit | edit source]
Plus zero and minus zero[edit | edit source]
Precision and formatted decimals[edit | edit source]
Rounding of close but inexact values[edit | edit source]
|
Techniques
[edit | edit source]For speed
[edit | edit source]Many comparisons
[edit | edit source]
If your code contains many numerical comparisons, it may be tempting to put: # python code.
if a == b == c == d == e == f == g == h == 0 :
pass
If all values
# python code.
if 0 == f == a == b == c == d == e == g == h :
pass
|
Divide by 2
[edit | edit source]
Division by 2 seems simple enough: # python code.
a = b / 2
Divisions are time consuming. If b is a large Decimal number, the following code is faster: # python code.
a = D('0.5') * b
If b is # python code.
a = b >> 1
Also, right shift preserves precision of # python code.
>>> b = 12345678901234567890123456789
>>> a = b/2 ; a
6.172839450617284e+27
>>> a = b >> 1 ; a
6172839450617283945061728394
To preserve rightmost bit: # python code.
>>> b = 12345678901234567890123456789
>>> rightbit = b & 1 ; rightbit
1
>>> b >>= 1 ; b
6172839450617283945061728394
|
Assignments
[edit | edit source]
>>> + 44 ; eval(' - 33 ') ; eval(' - 0003.e-002 ') ; eval(' + 0003.00000E-002 ') ; eval(' + 0003.12E0073 ')
>>> + 4 + 1j*3 ; complex(2,-3) ; complex("2+3j") ; eval("2+3J") ; complex('-3',7j)
>>> bool(6) ; bool(0) ; bool('0') ; '123'+0 ; bool('123') + 1 ; 1+3 == 4 ; 2**2 == -3
if sum != count / 10_000_000_000 :
This could be written as: if sum != count * increment :
However: >>> 5*(1e-10) ; 6*(1e-10) ; 7*(1e-10)
5e-10
6e-10
7.000000000000001e-10
>>>
>>> 7 / (1e10)
7e-10
>>>
How would you write the line
For greater precision than is available with floating point arithmetic, use Python's decimal module to calculate |
Further Reading or Review
[edit | edit source]
|
References
[edit | edit source]- ↑ http://docs.python.org/3.4/library/functions.html#bool
- ↑ https://docs.python.org/3/reference/lexical_analysis.html#integer-literals
- ↑ https://docs.python.org/3/reference/lexical_analysis.html#integer-literals
- ↑ https://docs.python.org/3/reference/lexical_analysis.html#integer-literals
- ↑ Wikipedia:Real number
- ↑ https://en.wikipedia.org/wiki/Floating-point_arithmetic#Floating-point_numbers
- ↑ http://docs.python.org/3.4/library/functions.html#bool
- ↑ https://docs.python.org/3//library/stdtypes.html#boolean-operations-and-or-not
Python's built-in functions:
"abs()", "bin()", "bool()", "complex()", "divmod()", "eval(expression, ....)", "float()", "hex()", "int()", "oct()", "pow()", "round()", "sys.float_info", "sys.int_info"
Python's documentation:
"3.1.1. Numbers", "Numeric Types", "Integer literals", "Floating point literals", "Imaginary literals", "Operator precedence", "Why are floating-point calculations so inaccurate?", "15. Floating Point Arithmetic: Issues and Limitations", "Decimal fixed point and floating point arithmetic", "4.4.2. Additional Methods on Integer Types", "4.4.3. Additional Methods on Float" "cmath.isclose(a, b, ....)"