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

Ron Adam ron3200 at gmail.com
Fri Feb 6 04:53:49 CET 2015



On 02/05/2015 06:44 PM, Steven D'Aprano wrote:
> On Thu, Feb 05, 2015 at 11:39:43AM -0800, Chris Barker wrote:
>
>>> > >- What if a tolerance is inf or nan? What if all args are inf or nan?
>> >
>> >0.0 < rel_tol < 1.0)
> I can just about see the point in restricting rel_tol to the closed
> interval 0...1, but not the open interval. Mathematically, setting the tolerance to 0
> should just degrade gracefully to exact equality, and a tolerance of
> 1 is nothing special at all.

I Agree.

> Values larger than 1 aren't often useful, but there really is no reason
> to exclude tolerances larger than 1. "Give or take 300%" (ie.
> rel_tol=3.0) is a pretty big tolerance, but it is well-defined: a
> difference of 299% is "close enough", 301% is "too far".

I agree here too.

The question I have is, is it better to get an excption, and possibly need 
to wrap the isclose() in a try-except, or better to let it go above 1?

If this is used in many places, I can see wrapping it in try-excepts as a 
bigger problem.  The same goes for when both tolerances are zero.


> You might argue that if you want exact equality, you shouldn't use a
> tolerance at all but just use == instead. But consider a case where
> you might be trying calculations repeatedly and decreasing the tolerance
> until it fails:
>
> # doesn't handle termination properly
> rel_tol = pick_some_number
> while close_to(something, something_else, rel_tol):
>      rel_tol /= 2
>
> In some cases, your calculation may actually be exact, and it simplies
> the code if close_to(a, b, rel_tol) degrades nicely to handle the exact
> case rather than you needing to switch between close_to and == by hand.

Yes.. +1

> Negative error tolerances, on the other hand, do seem to be meaningless
> and should be prevented.

No they are well behaved.  It's just a measurement from the other end of 
the board. ;-)

       t = .1   (10 - 10 * .1,  10 + 10 * .1)
                (9, 11)

       t = -.1   (10 - 10 * -.1,  10 + 10 * -.1)
                 (11, 9)

So you get the same range either way.  It works out in the isclose 
calculations just fine,  a negative tolerance would give the same result as 
the positive of the same value.

Again, is this a place where we would want an exception, and would we need 
to write it like isclose(a, b, rel_tolerance=abs(t)) any place where the 
tolerance value might go negative, or would we like it to be more forgiving 
and easier to use?


>> >I've enforced this in the sample code -- it's not strictly necessary, but
>> >what does a negative relative tolerance mean, anyway, and a tolerance over
>> >1.0 has some odd behaviour with zero and could hardly be called "close"
>> >anyway. Text added to make this clear.
> I don't see why a tolerance over 1 should behave any more oddly with
> zero than a tolerance under 1.
>
> And as for "close", some people would argue that rel_tol=0.5 is "hardly
> close". Sometimes you might accept anything within an order of magnitude
> the expected value: anything up to 10*x is "close enough". Or 20 times.
> Who knows?
>
> (E.g. "guess the number of grains of sand on this beach".) Any upper
> limit you put in is completely arbitrary, and this function shouldn't be
> in the business of telling people how much error they are allowed to
> tolerate. It's easy for people to put in their own restrictions, but
> hard for them to work around yours.

I agree with this idea in principle.  I think weather we want to handle 
exceptions or not is more of an issue though.

If it does allow higher tolerances, then it does need to handle them 
correctly.  I don't see that as a difficult problem though.

I prefer the weak version myself.  Because if you graph the result of True 
values for rel_tolerance=1.  You get a graph where all like signed numbers 
are close.   A tolerance of .5 gives a graph of the fifty percent of middle 
like signed numbers.  And you can think of it as a percentage of the larger 
value.  Which tends to be easier than thinking about percent increase.

Also this recommends using this method.

     http://c-faq.com/fp/fpequal.html



So for the weak method and increasing values of tolerance:

A graphs of all True values fill from the like signed diagonal and 
converges on the unlike signed diagonal when rel_tolerance=2.  Values above 
that don't change anything,  they are all still true.  So rel_tolerance=1 
gives True for all like signed numbers.  And all unlike signed number is 
the graph rel_tolerance=2 minus the graph of rel_tolerance=1.


For the strong method, and increasing values of tolerance:

The graphs fill from the like signed diagonal axis first until t=2, (the 
like signed quadrants are not filled yet), then it also starts filling from 
the unlike signed axis. So t=3 shows an X pattern, with a wider area in the 
like signed quadrants.  I'm not sure what the upper value of t is needed so 
all values are True?  It may be inf,

Keep in mind these graphs are the sum of comparing all the different values 
given a particular value of t.

So it there isn't a reason to limit the range of the tolerance, it will 
work just fine if we don't.

It's much harder to pick a strong tolerance that gives some sort of 
specific behaviour. For that reason I prefer the weak version much more 
than the strong version.

Cheers,
    Ron







More information about the Python-ideas mailing list