[Python-ideas] Way to check for floating point "closeness"?

Ron Adam ron3200 at gmail.com
Thu Jan 15 03:13:42 CET 2015



On 01/14/2015 06:04 PM, Steven D'Aprano wrote:
> On Wed, Jan 14, 2015 at 03:18:17PM -0600, Ron Adam wrote:
>> >
>> >
>> >On 01/14/2015 04:39 AM, Steven D'Aprano wrote:
> [...]
>>> > >Suppose you have a value which should be exactly 0.5, but you calculate
>>> > >it as 0.51. Then the absolute error is 0.51-0.5 = 0.01, and the relative
>>> > >error is 0.01/0.5 = 0.02 (or 2%).
>>> > >
>>> > >But consider two values, 0.0 and 0.1. Then the absolute error is 0.1-0.0
>>> > >= 0.1, and the relative error is 0.1/0.0 which is infinite.
>>> > >
>>> > >So 0.0 and -0.0 are problematic when dealing with relative errors.
>> >
>> >I'm not sure why, but it seems like something here is out of place to me.
>> >Either it's not something that would come up, or it's something you would
>> >expect and handle differently.  Or it's would be an error, signalling you
>> >need to handle it differently.
> You're going to have to explain what you think is "out of place".
>
> Of course doing division by zero is an error. The whole point of this
> discussion is that you cannot talk about errors relative to zero. At
> zero, you can still have an absolute error, but you cannot have a
> relative error. The definition of the error between two numeric values:
>
> def abserr(a, b):
>      return abs(a - b)
>
> def relerr(a, b):
>      return abs(a - b)/min(abs(a), abs(b))
>
>
> If either a or b is zero, relarr() fails. This is a mathematical
> limitation, not a bug to be fixed.


I would probably write like this.

     def relerr(actual, expected):
         return abs(actual - expected)/expected

Where expected is a non-zero reference value.

it needs a third value.  In the above the expected value is the scaler.
And then use it this way to measure a resistors tolerance.

     if relerr(218.345, 220) <= 0.05:
         print("Good")
     else:
         print("Bad")


Note that you would never compare to an expected value of zero.

     relerr(a - b, expected_feet) < tolerance   # relative feet from b

     relerr(a - 0, expected_feet) < tolerance   # relative feet from zero


     relerr(a - b, ulp)    # percentage of ulp's



What Chris is looking for is a way to get a closeness function that works 
most of the time. (He posted while I'm writing this.)

But I don't see how you can do that without specifying some scaler to give 
it context, and a tolerance.

      def is_close(a, b, unit, good):
          return (abs(a - b) / unit) <= good


      is_close(218.345, 220, 1, .05)   # OHMs

      is_close(a, b, ULP, 2)     # ULPs

      is_close(a, b, AU, .001)   # astronomical units


I don't see anyway to generalise those with just a function.

By using objects we can do a bit more.  I seem to recall coming across 
measurement objects some place.  They keep a bit more context with them.

Cheers,
    Ron

























More information about the Python-ideas mailing list