Are there data types with better precision than float?
Python's built-in float type has double precision (it's a C double in CPython, a Java double in Jython). If you need more precision, get NumPy and use its numpy.float128.
Decimal datatype
Unlike hardware based binary floating point, the decimal module has a user alterable precision (defaulting to 28 places) which can be as large as needed for a given problem.
If you are pressed by performance issuses, have a look at GMPY
For some applications you can use Fraction instead of floating-point numbers.
>>> from fractions import Fraction
>>> Fraction(1, 3**54)
Fraction(1, 58149737003040059690390169)
(For other applications, there's decimal, as suggested out by the other responses.)
May be you need Decimal
>>> from decimal import Decimal
>>> Decimal(2.675)
Decimal('2.67499999999999982236431605997495353221893310546875')
Floating Point Arithmetic
Here is my solution. I first create random numbers with random.uniform, format them in to string with double precision and then convert them back to float. You can adjust the precision by changing '.2f' to '.3f' etc..
import random
from decimal import Decimal
GndSpeedHigh = float(format(Decimal(random.uniform(5, 25)), '.2f'))
GndSpeedLow = float(format(Decimal(random.uniform(2, GndSpeedHigh)), '.2f'))
GndSpeedMean = float(Decimal(format(GndSpeedHigh + GndSpeedLow) / 2, '.2f')))
print(GndSpeedMean)
Related
I am trying to play with Prime numbers.
I tried this:
p = 2**82_589_933 -1
p = p/7
And got this error:
OverflowError: integer division result too large for a float
Also tried this:
A = 1.306377883863080690468614492602605712916784585156713644368053759966434053766826598821501403701197395707296960938103086882238861447816353486887133922146194353457871100331881405093575355831932648017213832361522359062218601610856679057215197976095161992952797079925631721527841237130765849112456317518426331056521535131866841550790793723859233522084218420405320517689026025793443008695290636205698968726212274997876664385157661914387728449820775905648255609150041237885247936260880466881540643744253401310736114409413765036437930126767211713103026522838661546668804874760951441079075406984172603473107746
print(A)
But it prints: 1.3063778838630806
Is there a way to work with high precision floating point numbers ?
Yes, use Decimal numbers, not floating point:
https://docs.python.org/3/library/decimal.html
The decimal module provides support for fast correctly-rounded decimal
floating point arithmetic. It offers several advantages over the float
datatype:
Decimal “is based on a floating-point model which was designed with
people in mind, and necessarily has a paramount guiding principle –
computers must provide an arithmetic that works in the same way as the
arithmetic that people learn at school.” – excerpt from the decimal
arithmetic specification.
Decimal numbers can be represented exactly. In contrast, numbers like
1.1 and 2.2 do not have exact representations in binary floating point. End users typically would not expect 1.1 + 2.2 to display as
3.3000000000000003 as it does with binary floating point.
(...)
Unlike hardware based binary floating point, the decimal module has a
user alterable precision (defaulting to 28 places) which can be as
large as needed for a given problem:
>>> from decimal import *
>>> getcontext().prec = 6
>>> Decimal(1) / Decimal(7)
Decimal('0.142857')
>>> getcontext().prec = 28
>>> Decimal(1) / Decimal(7)
Decimal('0.1428571428571428571428571429')
I can't figure out how to specify my decimal precision for logs, importing decimal and setting the context does not affect the log functions.
from decimal import *
getcontext().prec = 54
print(Decimal(197)/ Decimal(83))
2.37349397590361445783132530120481927710843373493975904
print(math.log(Decimal(197)))
5.2832037287379885
I would like to set a high precision for functions other than fractions. Python 3 btw.
Possible solutions:
String formatting:
f-strings have the benefit of not requiring another package to be imported
f-Strings: A New and Improved Way to Format Strings in Python
Extend the decimal places shown, to the same extent as the decimal module
x = 4567.09710599898797936589076897
y = 2445.89790870380808990080797897
Lengthen:
print(f'{(x/y):.054f}')
>>> 1.867247643389702504990168563381303101778030395507812500
calculation = math.log(197)
print(f'{calculation:.050f}')
>>> 5.28320372873798849155946300015784800052642822265625
Shorten:
print(f'{(x/y):.02f}')
>>> 1.87
numpy:
numpy.round
Shorten:
print(np.round(x/y, 2))
>>> 1.87
Lengthen:
numpy does not extend the precision shown, beyond what's shown by python.
print(np.around(x/y, 54))
>>> 1.8672476433897025
print(x/y)
>>> 1.8672476433897025
decimal Module:
Decimal fixed point and floating point arithmetic
Example from the question:
print(math.log(197))
>>> 5.2832037287379885
print(math.log(Decimal(197.0)))
>>> 5.2832037287379885
print(Decimal(math.log(197)))
>>> 5.28320372873798849155946300015784800052642822265625
print(Decimal(197).ln())
>>> 5.283203728737988506779797329
print(f'{math.log(197):.050f}')
>>> 5.28320372873798849155946300015784800052642822265625
Notes:
Either method can be used to format the number to the required decimal place, prior to writing into a log.
Caveat: due to the way numbers are represented in computers, I'm doubtful if increasing the number of decimal places shown, increases the precision.
Floating Point Arithmetic: Issues and Limitations
Using f-strings provided the same final output precision as using the decimal module.
I need to truncate decimal types without rounding & retain the decimal type, in the most processor efficient way possible.
The Math options I believe returns a float.
The quantize option returns a rounded number I believe.
Str options are way to processor costly.
Is there a simple, direct way to simply cut the digits off a decimal type past a specified decimal length?
The quantize method does have a rounding parameter which controls how the value is rounded. The ROUND_DOWN option seems to do what you want:
ROUND_DOWN (towards zero)
from decimal import Decimal, ROUND_DOWN
def truncate_decimal(d, places):
"""Truncate Decimal d to the given number of places.
>>> truncate_decimal(Decimal('1.234567'), 4)
Decimal('1.2345')
>>> truncate_decimal(Decimal('-0.999'), 1)
Decimal('-0.9')
"""
return d.quantize(Decimal(10) ** -places, rounding=ROUND_DOWN)
If I understand you correctly you can use divmod (it's a build-in function). It splits a number into integer and decimal parts:
>>> import decimal
>>> d1 = decimal.Decimal(3.14)
>>> divmod(d1, 1)[0]
Decimal('3')
>>> d2 = decimal.Decimal(5.64)
>>> divmod(d2, 1)[0]
Decimal('5')
To cut off decimals past (for example) the second decimal place:
from math import floor
x = 3.14159
x2 = floor(x * 100) / 100
I have a list and it contains a certain number '5.74536541' in it which I convert to a float.
I am printing it out in Python 3 using ("%0.2f" % (variable)) but it always prints out 5.75 instead of 5.74.
I know you're thinking who cares, but it is for a currency converter program and I don't want the currencies to round up/down but to be exact.
How can I keep it from rounding but also keep the 2 decimal places?
You shouldn't use floating point numbers for currency, due to rounding errors like you mentioned.
Your best bet is to use a fixed-precision decimal where you also have full control over how rounding and truncation works. From the docs:
>>> from decimal import *
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999,
capitals=1, flags=[], traps=[Overflow, DivisionByZero,
InvalidOperation])
>>> getcontext().prec = 6
>>> Decimal('3.0')
Decimal('3.0')
>>> Decimal('3.1415926535')
Decimal('3.1415926535')
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal('5.85987')
>>> getcontext().rounding = ROUND_UP
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal('5.85988')
You should represent all currency-based values internally as Decimals with a high precision (the standard level of precision should be fine in your case - just leave the prec alone!). If you want to print a nicely formatted dollars and cents value to the user, using the locale module is a straightforward way to do this.
Be careful when printing as you will have to quantize the Decimal down to the correct number of places for display or the rounding will not be based on your Decimal context! You should only perform the quantize step for final display or for a single, final value - all intermediate steps should use high-precision Decimals to make any operations as accurate as possible.
>>> from decimal import *
>>> import locale
>>> locale.setlocale(locale.LC_ALL, '')
'en_AU.UTF-8'
>>> getcontext().rounding = ROUND_DOWN
>>> TWOPLACES = Decimal(10) ** -2
>>> var = Decimal('5.74536541')
Decimal('5.74536541')
>>> var.quantize(TWOPLACES)
Decimal('5.74')
>>> locale.currency(var.quantize(TWOPLACES))
'$5.74'
If you're dealing with currency and accuracy matters, don't use float, use decimal.
Take away the number mod 0.01
i.e.
rounded = number - (number % 0.01)
then print it the same as before.
This said, rounding down is not more accurate. Are you trying the old steal money from a bank by exploiting rounding errors scheme?
Floating point values are known as "useful approximations". Whatever you do to a floating point number—round it, truncate it, whatever—if the result is a floating point value, you don't get to decide how many digits to the right of the decimal point it has.
Never use floating point values for currency. See pydoc decimal, for example. Python's decimal module supports decimal fixed point and decimal floating point arithmetic.
Python docs warn about rounding floats.
Note The behavior of round() for floats can be surprising: for
example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This
is not a bug: it’s a result of the fact that most decimal fractions
can’t be represented exactly as a float.
If you're not careful, you'll be misled by the value that appears at the interpreter prompt.
Python only prints a decimal approximation to the true decimal value
of the binary approximation stored by the machine.
And
It’s important to realize that this is, in a real sense, an illusion:
the value in the machine is not exactly 1/10, you’re simply rounding
the display of the true machine value. This fact becomes apparent as
soon as you try to do arithmetic with these values
If the number is a string then truncate the string to only 2 characters after the decimal and then convert it to a float.
Otherwise multiply it with 10^n where n is the number of digits after the decimal and then divide your float by 10^n.
The built-in Python str() function outputs some weird results when passing in floats with many decimals. This is what happens:
>>> str(19.9999999999999999)
>>> '20.0'
I'm expecting to get:
>>> '19.9999999999999999'
Does anyone know why? and maybe workaround it?
Thanks!
It's not str() that rounds, it's the fact that you're using floats in the first place. Float types are fast, but have limited precision; in other words, they are imprecise by design. This applies to all programming languages. For more details on float quirks, please read "What Every Programmer Should Know About Floating-Point Arithmetic"
If you want to store and operate on precise numbers, use the decimal module:
>>> from decimal import Decimal
>>> str(Decimal('19.9999999999999999'))
'19.9999999999999999'
A float has 32 bits (in C at least). One of those bits is allocated for the sign, a few allocated for the mantissa, and a few allocated for the exponent. You can't fit every single decimal to an infinite number of digits into 32 bits. Therefore floating point numbers are heavily based on rounding.
If you try str(19.998), it will probably give you something at least close to 19.998 because 32 bits have enough precision to estimate that, but something like 19.999999999999999 is too precise to estimate in 32 bits, so it rounds to the nearest possible value, which happens to be 20.
Please note that this is a problem of understanding floating point (fixed-length) numbers. Most languages do exactly (or very similar to) what Python does.
Python float is IEEE 754 64-bit binary floating point. It is limited to 53 bits of precision i.e. slightly less than 16 decimal digits of precision. 19.9999999999999999 contains 18 decimal digits; it cannot be represented exactly as a float. float("19.9999999999999999") produces the nearest floating point value, which happens to be the same as float("20.0").
>>> float("19.9999999999999999") == float("20.0")
True
If by "many decimals" you mean "many digits after the decimal point", please be aware that the same "weird" results happen when there are many decimal digits before the decimal point:
>>> float("199999999999999999")
2e+17
If you want the full float precision, don't use str(), use repr():
>>> x = 1. / 3.
>>> str(x)
'0.333333333333'
>>> str(x).count('3')
12
>>> repr(x)
'0.3333333333333333'
>>> repr(x).count('3')
16
>>>
Update It's interesting how often decimal is prescribed as a cure-all for float-induced astonishment. This is often accompanied by simple examples like 0.1 + 0.1 + 0.1 != 0.3. Nobody stops to point out that decimal has its share of deficiencies e.g.
>>> (1.0 / 3.0) * 3.0
1.0
>>> (Decimal('1.0') / Decimal('3.0')) * Decimal('3.0')
Decimal('0.9999999999999999999999999999')
>>>
True, float is limited to 53 binary digits of precision. By default, decimal is limited to 28 decimal digits of precision.
>>> Decimal(2) / Decimal(3)
Decimal('0.6666666666666666666666666667')
>>>
You can change the limit, but it's still limited precision. You still need to know the characteristics of the number format to use it effectively without "astonishing" results, and the extra precision is bought by slower operation (unless you use the 3rd-party cdecimal module).
For any given binary floating point number, there is an infinite set of decimal fractions that, on input, round to that number. Python's str goes to some trouble to produce the shortest decimal fraction from this set; see GLS's paper http://kurtstephens.com/files/p372-steele.pdf for the general algorithm (IIRC they use a refinement that avoids arbitrary-precision math in most cases). You happened to input a decimal fraction that rounds to a float (IEEE double) whose shortest possible decimal fraction is not the same as the one you entered.