On Tue, Jan 27, 2015 at 9:07 AM, Chris Barker <chris.barker@noaa.gov> wrote:
On Tue, Jan 27, 2015 at 8:20 AM, Guido van Rossum <guido@python.org> wrote:
A) Which test do we use:
  1) The asymmetric test
  2) The "strong" test (minimum relative tolerance)
  3) The "weak" test (maximum relative tolerance)

The problem with this question is that, while it's easy to come up with examples where it may matter (e.g. 95 is within 5% of 100, but 100 is not within %5 of 95), in practice the tolerance is more likely to be 1e-8, in which case it doesn't matter.

Exactly why I'm happy with any of them. I'm trying to suss out whether anyone else has a reason to reject one or the others. If no one does, then we can just pick one.

By now can't you summarize the reasons that others have brought up?

 
B) Do we provide a non-zero default for the absolute tolerance? If so what should the value be? Remember that this serves primarily to provide a check against zero.

It feels like absolute tolerance is a completely different test. And it is a much simpler test for which w don't need a helper function -- it's just abs(x) < tolerance.

When does a program need *both* absolute and relative tolerance in a single test?

Because we want it to be able to do something sane when comparing to zero -- the abs_tolerance allows you to set a minimum tolerance that will do something reasonable near zero (we could use a zero_tolerance, as Nathaniel has suggested, instead, but that creates the incontinuity that , for example, 1e-12 is close to zero, but it is not close to 1e-100 -- I think that's a bad idea for a call with the same arguments). I spend a good while thinking about this and playing with it, and it became clear to me that this is the best way to go for a not-to-surprising result. And it's consistent with what numpy and Steven's statistics test code does.

I don't think you can have this always be sane. For someone who for whatever reason is manipulating quantities that are in the range of 1e-100, 1e-12 is about as large as infinity.

I think my reasoning comes down to the same rule I often use to decide whether we need one function or two -- if in every use case you always know whether you need version A or version B, then it's better to have two functions rather than a single one with a flag to request A or B.

And isn't it the case that whenever you are comparing to zero, you *know* that you are comparing to zero, and you *must* specify an absolute tolerance (otherwise it's not a use case at all)?

IIUC, numpy doesn't design APIs this way. They like to have swiss army knives that can do lots of different things, with lots of flags to request variant behavior. (I learned this from the discussion about linspace().) But IMO that goes back to an earlier, murkier tradition -- I recall using Fortran plot functions in the '80s that had 17 parameters. The reason for that style was that there was no concept of modules or packages, and hence there was only one namespace, shared between all possible libraries. So a new library would claim only a single name in the namespace and hook all sorts of functionality onto that single name. We don't have that problem in Python and hence I prefer clarity in functionality -- different functions for different behaviors, basically. (Maybe this should become the 20th line of the Zen of Python. :-)
 
Still TBD is what the default should be, though.
  
I still think this is better off as a recipe than as a helper function.

Are you prepared to reject the PEP? I'd prefer to give it this one last shot at determining if there really is no way to get consensus on a good-enough solution. I suspect there is a lot of bike shedding here -- people have ideas about what is best, and want to understand and talk about it, but that doesn't mean that they wouldn't rather see something else that nothing. -- that's certainly the case for me (both the bike shedding and the desire to see something ;-) )

Yes, I am prepared to reject the PEP. In fact any PEP is rejected by default if no consensus is obtained.
 

Evidence: The numpy version has its faults -- but it's widely used. assertAlmost Equal has even more faults (limitations, anyway) but it's also widely used. Boost has something in it, even though it's a one-liner. Clearly this is a useful functionality to have available.
 
IIUC Boost's approach is better than numpy. It has separate functions for is_close (relative) and is_small (absolute), because they are separate use cases. Its is_close uses the symmetric version (though there is a flag to choose between weak and strong, which sounds like overkill).
 
ChrisA is right -- I have not done a good job at steering the group toward a consensus.

It's not too late!

--
--Guido van Rossum (python.org/~guido)