-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a convenience function for floating-point comparisons #1441
Conversation
This was a challenge because it had to work in python2 and python3, which have almost opposite unicode models, and I couldn't use the six library. I'm also not sure the solution I found would work in python3 before python3.3, because I use the u'' string prefix which I think was initially not part of python3.
Overall nice work, ill take a deeper look tomorow |
Some random thoughts:
|
Nice work, thanks for the PR! 😄 |
|
||
def __repr__(self): | ||
from collections import Iterable | ||
utf_8 = lambda s: s.encode('utf-8') if sys.version_info.major == 2 else s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately this is not supported in py26: sys.version_info.major
. You will have to use sys.version_info[0] == 2
.
👍 |
@The-Compiler Thanks for pointing out
|
Thanks for the explanations! I don't have any hard feelings either way, but it's good to know how it relates to the "previous art" 😄 |
good research, i think its important to document those differences in a comprehensible manner |
Excellent summary! I agree with @RonnyPfannschmidt, I would strongly encourage you to add it to the documentation. |
This commit also: - Dramatically increases the number of unit tests , mostly by borrowing from the standard library's unit tests for math.isclose(). - Refactors approx() into two classes, one of which handles comparing individual numbers (ApproxNonIterable) and another which uses the first to compare individual numbers or sequences of numbers.
I think this branch is ready to merge. Let me know what you think. |
@@ -7,7 +7,8 @@ | |||
namespace in which your doctests run. | |||
Thanks `@milliams`_ for the complete PR (`#1428`_). | |||
|
|||
* | |||
* New ``approx()`` function for easily comparing floating-point numbers in | |||
tests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a "Thanks @kalekundert for the complete PR"
. 😄
Looks very good to me! 😁 |
I agree it seems ready to merge, after others take another look! |
return ', '.join(repr(x) for x in self.expected) | ||
|
||
def __eq__(self, actual): | ||
from collections import Iterable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not do import collections
at the top of the file instead?
This pull request adds a convenience class to assert that two floating-point numbers (or two sets of numbers) are equal to each other within some margin. You give the class the number you're expecting to get, and the class overrides the "==" operator to do the comparison behind the scenes with a reasonable margin for error. Most unit testing frameworks have some sort of
assertAlmostEqual()
function to provide this functionality, but to my knowledgepytest
doesn't have anything of the sort. I think this pull request provides a useful feature in a way that dovetails nicely with the waypytest
deconstructs assert statements.Tests and documentation are included. I ran all the tests on python2.7 and python3.4, but I'm a little worried that the way I use unicode in
approx.__repr__
might not work in python3.2. I copied most of the new documentation below for convenience:Due to the intricacies of floating-point arithmetic, numbers that we would intuitively expect to be the same are not always so:
This problem is commonly encountered when writing tests, e.g. when making sure that floating-point values are what you expect them to be. One way to deal with this problem is to assert that two floating-point numbers are equal to within some appropriate margin:
However, comparisons like this are tedious to write and difficult to understand. Furthermore, absolute comparisons like the one above are usually discouraged in favor of relative comparisons, which can't even be easily written on one line. The
approx
class provides a way to make floating-point comparisons that solves both these problems:approx
also makes is easy to compare ordered sets of numbers, which would otherwise be very tedious:By default,
approx
considers two numbers to be equal if the relative error between them is less than one part in a million (e.g.1e-6
). Relative error is defined asabs(x - a) / x
wherex
is the value you're expecting anda
is the value you're comparing to. This definition breaks down when the numbers being compared get very close to zero, soapprox
will also consider two numbers to be equal if the absolute difference between them is less than one part in a trillion (e.g.1e-12
).