python unittest2 assertAlmostEqual with `places` works incorrectly - python

I am dealing with the following problem with unittest2:
assertAlmostEqual(69.88, 69.875, places=2) # returns True
but
assertAlmostEqual(1.28, 1.275, places=2) # returns False
I think problem is in the assertAlmostEqual method:
def assertAlmostEqual(self, first, second, places=None, ...):
if first == second:
# shortcut
return
...
if delta is not None:
...
else:
if places is None:
places = 7
if round(abs(second-first), places) == 0:
return
...
raise self.failureException(msg)
Should it instead be:
if abs(round(second, places) - round(first, places)) == 0
return

Your proposed fix doesn't make any difference, as you can easily demonstrate:
>>> places = 2
>>> first, second = 69.88, 69.875
>>> round(abs(second-first), places)
0.0
>>> abs(round(second, places) - round(first, places))
0.0
This is a problem with floating point precision, see e.g. Is floating point math broken? 69.88 cannot be represented exactly:
>>> "{:.40f}".format(69.88)
'69.8799999999999954525264911353588104248047'

The difference in the second example is
0.005
And even without mentioned biases of floating points result of round will be 0.01, so these numbers really different with 2-places precision
This method compares difference between numbers. It is kinda standard of comparing float numbers actually
So the problem is not with implementation, but with you expectations, that is different from common float comparison

Related

Python Exponentiating Operator

Could someone help me to understand the following statement?
Why this is True:
3.0 == 3
but this one not?
4 ** 0.5 != 2
(4**0.5 = 2.0, and according to above statement 2.0 is equal to 2), but I get False. WHy?
The != operator does the opposite of ==.
(In your first example, you wrote = but I think you mean ==.)
With !=, it evaluates True if the two numbers are not equal to each other, otherwise it is False. (Here, "equal" can include an integer and a floating point number having the same numerical value.)
So here:
>>> 4 ** 0.5 != 2
False
>>> 4 ** 0.5 == 2
True
Unlike some languages, Python does not care about floats and ints when it comes to comparing. It will treat 4 and 4.0 as the exact same when comparing them to other numbers. You can debate the reliability of this feature of Python, but for the purposes of answering this question this is relevant.
Now we know this, we can see that 4 * .5 is the same is 2.0 which is the same as 2. The actual reason the second argument is false has nothing to do with the number being a float or not, but actually to do with the operator you are using.
== will return true if both sides of the equation ARE equal, whereas != will return true if both sides of the equation ARE NOT equal.
The second statement is using the != operator and so is returning false because both sides are equal. If both sides where not equal, it would return false.
Hope this cleared things up!

How does Python compare 'int' to 'float' objects?

The documentation about numeric types states that:
Python fully supports mixed arithmetic: when a binary arithmetic operator has operands of different numeric types, the operand with the “narrower” type is widened to that of the other, where integer is narrower than floating point, which is narrower than complex. Comparisons between numbers of mixed type use the same rule.
This is supported by the following behavior:
>>> int.__eq__(1, 1.0)
NotImplemented
>>> float.__eq__(1.0, 1)
True
However for large integer numbers something else seems to happen since they won't compare equal unless explicitly converted to float:
>>> n = 3**64
>>> float(n) == n
False
>>> float(n) == float(n)
True
On the other hand, for powers of 2, this doesn't seem to be a problem:
>>> n = 2**512
>>> float(n) == n
True
Since the documentation implies that int is "widened" (I assume converted / cast?) to float I'd expect float(n) == n and float(n) == float(n) to be similar but the above example with n = 3**64 suggests differently. So what rules does Python use to compare int to float (or mixed numeric types in general)?
Tested with CPython 3.7.3 from Anaconda and PyPy 7.3.0 (Python 3.6.9).
The language specification on value comparisons contains the following paragraph:
Numbers of built-in numeric types (Numeric Types — int, float, complex) and of the standard library types fractions.Fraction and decimal.Decimal can be compared within and across their types, with the restriction that complex numbers do not support order comparison. Within the limits of the types involved, they compare mathematically (algorithmically) correct without loss of precision.
This means when two numeric types are compared, the actual (mathematical) numbers that are represented by these objects are compared. For example the numeral 16677181699666569.0 (which is 3**34) represents the number 16677181699666569 and even though in "float-space" there is no difference between this number and 16677181699666568.0 (3**34 - 1) they do represent different numbers. Due to limited floating point precision, on a 64-bit architecture, the value float(3**34) will be stored as 16677181699666568 and hence it represents a different number than the integer numeral 16677181699666569. For that reason we have float(3**34) != 3**34 which performs a comparison without loss of precision.
This property is important in order to guarantee transitivity of the equivalence relation of numeric types. If int to float comparison would give similar results as if the int object would be converted to a float object then the transitive relation would be invalidated:
>>> class Float(float):
... def __eq__(self, other):
... return super().__eq__(float(other))
...
>>> a = 3**34 - 1
>>> b = Float(3**34)
>>> c = 3**34
>>> a == b
True
>>> b == c
True
>>> a == c # transitivity demands that this holds true
False
The float.__eq__ implementation on the other hand, which considers the represented mathematical numbers, doesn't infringe that requirement:
>>> a = 3**34 - 1
>>> b = float(3**34)
>>> c = 3**34
>>> a == b
True
>>> b == c
False
>>> a == c
False
As a result of missing transitivity the order of the following list won't be changed by sorting (since all consecutive numbers appear to be equal):
>>> class Float(float):
... def __lt__(self, other):
... return super().__lt__(float(other))
... def __eq__(self, other):
... return super().__eq__(float(other))
...
>>> numbers = [3**34, Float(3**34), 3**34 - 1]
>>> sorted(numbers) == numbers
True
Using float on the other hand, the order is reversed:
>>> numbers = [3**34, float(3**34), 3**34 - 1]
>>> sorted(numbers) == numbers[::-1]
True

Best way to see if a number is fractional or not

I'm looking to differentiate between a number like
2.0 or 2 and an actual fractional number such as 2.4. What would be the best way to do this? Currently I'm doing:
def is_fractional(num):
if not str(num).replace('.','').isdigit(): return
return float(num) != int(num)
>>> is_fractional(2)
False
>>> is_fractional(2.1)
True
>>> is_fractional(2.0)
False
>>> is_fractional('a')
>>>
That operation is built-in:
>>> 5.0.is_integer()
True
>>> 5.00000001.is_integer()
False
>>> 4.9999999.is_integer()
False
Documentation is here.
ADDENDUM
The initial solution only works for float. Here's a more complete answer, with tests:
from decimal import Decimal
def is_integer(x):
if isinstance(x, int):
return True
elif isinstance(x, float):
return x.is_integer()
elif isinstance(x, Decimal):
return x.as_integer_ratio()[1] == 1
return False
good = [
0,
0.0,
3,
-9999999999999999999999,
-2.0000000000000,
Decimal("3.000000"),
Decimal("-9")
]
bad = [
-9.99999999999999,
"dogs",
Decimal("-4.00000000000000000000000000000000001"),
Decimal("0.99999999999999999999999999999999999")
]
for x in good:
assert is_integer(x)
for x in bad:
assert not is_integer(x)
print("All tests passed")
If some of your numbers are decimal.Decimals, they might have range issues where conversion to float fails, or drops the fractional part that actually exists, depending on their precision:
>>> import decimal
>>> x = decimal.Decimal('1.00000000000000000000000000000000000001')
>>> str(x)
'1.00000000000000000000000000000000000001'
>>> float(x).is_integer()
True
>>> y = decimal.Decimal('1e5000')
>>> str(y)
'1E+5000'
>>> float(y)
inf
The str method will generally work (modulo problem cases like the one illustrated above), so you could stick with that, but it might be better to attempt to use is_integer and use a fallback if that fails:
try:
return x.is_integer()
except AttributeError:
pass
(as others note, you'll need to check for int and long here as well, if those are allowed types, since they are integers by definition but lack an is_integer attribute).
At this point, it's worth considering all of the other answers, but here's a specific decimal.Decimal handler:
# optional: special case decimal.Decimal here
try:
as_tuple = x.as_tuple()
trailing0s = len(list(itertools.takewhile(lambda i: i == 0, reversed(as_tuple[1]))))
return as_tuple[2] + trailing0s < 0
except (AttributeError, IndexError): # no as_tuple, or not 3 elements long, etc
pass
Why do not check if the difference between the truncation to integer and the exact value is not zero?
is_frac = lambda x: int(x)-x != 0
Python includes a fractions module that generates fractions (rational numbers) from strings, floats, integers, and much more. Just create a Fraction and check whether its denominator is other than 1 (the Fraction constructor will automatically reduce the number to lowest terms):
from fractions import Fraction
def is_fractional(num):
return Fraction(num).denominator != 1
Note that the method above may raise an exception if the conversion to a Fraction fails. In this case, it's not known whether the object is fractional.
If you are dealing with decimal module or with a float object, you can do this easily:
def is_factional(num):
return isinstance(num, (float, Decimal))
Here is one way to do it (assuming e.g. 2/2 is not "fractional" in the sense you have in mind):
# could also extend to other numeric types numpy.float32
from decimal import Decimal
def is_frac(n):
numeric_types = (int, float, Decimal)
assert isinstance(n, numeric_types), 'n must be numeric :/'
# (ints are never fractions)
if type(n) is int: return False
return n != float(int(n))
# various sorts of numbers
ns = [-1, -1.0, 0, 0.1, 1, 1.0, 1., 2.3, 1e0, 1e3, 1.1e3,
Decimal(3), Decimal(3.0), Decimal(3.1)]
# confirm that values are as expected
dict(zip(ns, [is_frac(n) for n in ns]))
This will only work if n is an int or a float or decimal.Decimal. But you could extend it to handle other numeric types such as numpy.float64 or numpy.int32 by just including them in numeric_types.

Float comparison (1.0 == 1.0) always false

I'm using the following function in Python 2.7.3 and Kivy 1.8.0 to fade-in a Grid widget:
def __init__(self, **kwargs):
# ...Init parent class here...
self.grid.opacity = 0.0
Clock.schedule_interval(self.show, 1 / 10)
def show(self, value):
if self.grid.opacity == 1.0:
return False
else:
self.grid.opacity += 0.1
show() is executed infinitely, self.grid.opacity == 1.0 always returs False, so the scheduler never removes the function.
I thought, and the documentation says, that opacity is a NumericProperty which defaults to 1.0, but I'm changing its value right before show() is called.
This is what I've tried:
if self.grid.opacity == NumericProperty(1.0):
if float(self.grid.opacity) == 1.0:
It doesn't work. Also, I'm sure self.grid.opacity is 1.0 and type() retrieves float right before I make the comparison.
This works:
if str(self.grid.opacity) == "1.0":
But obviously I don't like this solution.
Any ideas?
It is probably not Python specific. Read What Every Programmer Should Know About Floating-Point Arithmetic.
0.1 is not exactly representable as a IEEE754 double-precision floating point. So I guess that the floating point (parsed from) 0.1 (which is not exactly one tenth) is not converted as a string "0.1"
Might be a float comparison issue. I don't know the application, but float's are never exact, so testing them for equality can cause problems. You can try something like:
if abs(float(self.grid.opacity) - 1.0) < .001:
pass
An example of funny float behavior, at least on my setup:
>>> .1 + .1 + .1 == .3
False
>>> .1 + .1 == .2
True
This is your problem:
>>> q=0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1
>>> str(q)
'1.0'
>>> q
0.9999999999999999
>>> q==1
False
Bottom line never compare floats with ==, period.
As others have stated, the problem is due to the way floating point numbers are stored. While you could try to use workarounds, there's a better way to do this: Animation.
In __init__:
self.grid.opacity = 0
anim = Animation(opacity=1)
anim.start(self.grid)
#Basile Starynkevitch answers why this is happening, the nature of floating point numbers is at work here. The general form for doing this kind of comparison is:
abs(numberA - numberB) <= SOMEEPSILON
where SOMEEPSILON is a number you deem to be an acceptable margin.
If you're working with smaller numbers and not worried about a rounding error you can sys.float_info.epsilon
So as I commented by combining the two you get:
abs(self.grid.opacity- 1.0) <= sys.float_info.epsilon
The definition of epsilon's value in the docs is:
difference between 1 and the least value greater than 1 that is representable as a float
Which is a another way of saying, the smallest value between 1 and the number right before it.
So for example, if python could only represent numbers up to 2 decimal places, epsilon would be the difference between 1.00 and 0.99 (in reality the value is much smaller than that)

python Decimal - checking if integer

I am using the Decimal library in Python, and printing out the values using
format(value, 'f'), where value is a Decimal. I get numbers in the form 10.00000, which reflects the precision on the decimal. I know that float supports is_integer, but there seems to be a lack of a similar API for decimals. I was wondering if there was a way around this.
You could use the modulo operation to check if there is a non-integer remainder:
>>> from decimal import Decimal
>>> Decimal('3.14') % 1 == 0
False
>>> Decimal('3') % 1 == 0
True
>>> Decimal('3.0') % 1 == 0
True
Try math.floor(val) == val or val == int(val).
As of Python 3.6, Decimal has a method as_integer_ratio().
as_integer_ratio() returns a (numerator, denominator) tuple. If the denominator is 1, then the value is an integer.
>>> from decimal import Decimal
>>> Decimal("123.456").as_integer_ratio()[1] == 1
False
>>> Decimal("123.000").as_integer_ratio()[1] == 1
True
The mathematical solution is to convert your decimal number to integer and then test its equality with your number.
Since Decimal can have an arbitrary precision, you should not convert it to int or float.
Fortunately, the Decimalclass has a to_integral_value which make the conversion for you. You can adopt a solution like this:
def is_integer(d):
return d == d.to_integral_value()
Example:
from decimal import Decimal
d_int = Decimal(3)
assert is_integer(d_int)
d_float = Decimal(3.1415)
assert not is_integer(d_float)
Decimal does have a "hidden" method called _isinteger() that works kind of the like the float's is_integer() method:
>>> Decimal(1)._isinteger()
True
>>> Decimal(1.1)._isinteger()
Traceback (most recent call last):
File "C:\Program Files (x86)\Wing IDE 4.1\src\debug\tserver\_sandbox.py", line 1, in <module>
# Used internally for debug sandbox under external interpreter
File "C:\Python26\Lib\decimal.py", line 649, in __new__
"First convert the float to a string")
TypeError: Cannot convert float to Decimal. First convert the float to a string
As you can see, you would have to catch an exception though. Alternatively, you could do the test on the value BEFORE you pass it to Decimal using the float's method as you mentioned or by using isinstance.
You can call as_tuple() on a Decimal object to get the sign, the sequence of digits, and the exponent which together define the Decimal value.
If the exponent of a normalized Decimal is non-negative, then your value doesn't have a fractional component, i.e., it is an integer. So you can check for this very easily:
def is_integer(dec):
"""True if the given Decimal value is an integer, False otherwise."""
return dec.normalize().as_tuple()[2] >= 0
Try it and see:
from decimal import Decimal
decimals = [
Decimal('0'),
Decimal('0.0000'),
Decimal('1'),
Decimal('-1'),
Decimal('1000000'),
Decimal('0.1'),
Decimal('-0.0000000009'),
Decimal('32.4')]
for d in decimals:
print("Is {} an integer? {}".format(d, is_integer(d)))
Is 0 an integer? True
Is 0.0000 an integer? True
Is 1 an integer? True
Is -1 an integer? True
Is 1000000 an integer? True
Is 0.1 an integer? False
Is -9E-10 an integer? False
Is 32.4 an integer? False
Building on what was said above, I used:
>>> not 2.5 % 1
False
>>> not 1.0 % 1
True
>>> not 14.000001 % 1
False
>>> not 2.00000000 % 1
True
So you can use the following one liner:
not value % 1
It will provide you with your desired bool.
Decimal class has a function to_integral_exact which converts decimal to an integer and also signals if there were any non-zero digits discarded.
https://docs.python.org/3/library/decimal.html#decimal.Decimal.to_integral_exact
Using this information we can implment float's is_integer for Decimal too:
import decimal
def is_integer(value: decimal.Decimal) -> bool:
is_it = True
context = decimal.getcontext()
context.clear_flags()
exact = value.to_integral_exact()
if context.flags[decimal.Inexact]:
is_it = False
context.clear_flags()
return is_it
Using the function above:
# tested on Python 3.9
>>> is_integer(decimal.Decimal("0.23"))
False
>>> is_integer(decimal.Decimal("5.0000"))
True
>>> is_integer(decimal.Decimal("5.0001"))
False
Yet another way to do it is to use the quantize() method which is generally used to round decimals:
>>> from decimal import Decimal
>>> for d in (Decimal('2.72'), Decimal('3.14'), Decimal('3.0'), Decimal('3')):
... d == d.quantize(Decimal('1'))
...
False
False
True
True

Categories

Resources