[Python-ideas] PEP 485: A Function for testing approximate equality

Andrew Barnert abarnert at yahoo.com
Fri Feb 6 07:12:23 CET 2015


On Feb 5, 2015, at 16:53, Chris Barker <chris.barker at noaa.gov> wrote:

> On Thu, Feb 5, 2015 at 2:46 PM, Andrew Barnert <abarnert at yahoo.com> wrote:
>> >Non-float types
>> >---------------
>> >
>> >The primary use-case is expected to be floating point numbers.
>> >However, users may want to compare other numeric types similarly. In
>> >theory, it should work for any type that supports ``abs()``,
>> >comparisons, and subtraction.  The code will be written and tested to
>> 
>> >accommodate these types:
>> 
>> Surely the type also has to support multiplication (or division, if you choose to implement it that way) as well, right?
> 
> yes, multiplication, not sure how I  missed that. No division necessary.
>  
>> Also, are you sure your implementation doesn't need any explicit isinf/isnan checks? You mentioned something about cmath.isnan for complex numbers.
> 
> yeah, there's that. Though it seems to work fine for Fraction and Decimal so far -- I'm taking a TDD approach -- if it works with a test, I don't worry about it ;-)

Well, it would be nice if the docs could list the requirements on the type. It should be pretty simple to get them just by instrumenting a numeric type and seeing what gets called, if it isn't obvious enough from the code.

It would be even nicer if it happened to be a subset of the methods of numbers.Complex, so you could just say any Complex or Complex-like type (IIRC, Decimal doesn't subclass that because it doesn't quite qualify?) and most people could stop reading there. (Maybe you could even use that as the static type in the annotations/stub?) But if that isn't possible, that's fine.

> This brings up a bigger issue, that I'm pretty nuetral on:
> 
> What to do about duck-typing and various types it may get called with? My intent was to count of duck typing completely:

I think that makes sense--if it's possible, do that; if you have to add any special casing for any important type, I guess you have to, but hopefully you don't.

Calling math.isnan(abs(x)) instead of math.isnan(x) should work with any Complex, and with Decimal. 

Or you can even try isnan and on TypeError assume it's not nan, and then it'll work on "number-ish" types too.

The zero-construction issue might also show up with some number-ish types, but I'd guess that most types that have __float__ also have a constructor from float, so that probably won't hurt much.

The other requirements (that __sub__ and __mul__ are sensible, __abs__ returns a non-complex-ish version of the same type, etc.) all seem like they shouldn't be a problem at all for anything at all number-ish.

> I can't possible test with all types, so what else to do?

You can build a custom stupid
numeric type that only implements exactly what you claim to require. That's good evidence your duck typing and your documentation match.

Also, it might be worth testing what happens with, say, a numpy array. That seems be a good test of the "it works it raises, for any reasonable type". (Testing a numpy matrix might be a further useful test, except I'm not sure matrix counts as a reasonable type.)

> Would datetime.timedetla be worth explicitly supporting?

I don't think it's important enough. If you _implicitly_ support it through duck typing, that's cool (and a good sign that it'll work on many reasonable third-party types), but I wouldn't put extra work into it, especially not special casing, especially if you don't need any special casing for the important types.

> the concept of a two datetimes being relatively close doesn't make any sense -- relative to what????

Well, _absolute_ tolerance for datetimes makes sense (I've even used it in real code...), but since isclose is primarily a relative tolerance function, yeah, definitely don't try to make that work.

> And abs() fails for datetime already. I'll check timedelta -- it should work, I  think -- abs() does anyway.

But I'm pretty sure math.isnan(abs(x)) doesn't. That's exactly what the try/except could solve, if you want to solve it.

>> >How much difference does it make?
>> 
>> >---------------------------------
>> 
>> This section relies on transformations that are valid for reals but not for floats. In particular:
> 
> does this effect the result? I also assumed a and b are positive and a>b, just to make all the math easier (and easier to write). I'm not really trying to do a proper rigorous formal proof here. And it does check out computationally -- at least with comparing to 1.0,

Well, that's the whole point--when your values are all around 1.0, float issues are a whole lot simpler. Does it work for extremal and near-extremal values and values about 1e9 away from the extremes? If you're trying to make the point (semi-)formally by deriving based on reals and then testing for rounding, underflow, and overflow, that's what you need to test on.

On the other hand, I think (as you hint later) that it might be better just to not try to make the point formally in the PEP.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20150205/0296f679/attachment.html>


More information about the Python-ideas mailing list