Is there a “normal” EqualQ function in Mathematica?

百般思念 提交于 2019-11-30 00:46:29
In[12]:= MyEqual[x_, y_] := Order[x, y] == 0

In[13]:= MyEqual[1.0000000000000021, 1.0000000000000022]

Out[13]= False

In[14]:= MyEqual[1.0000000000000021, 1.0000000000000021]

Out[14]= True

This tests if two object are identical, since 1.0000000000000021 and 1.000000000000002100 differs in precision they won't be considered as identical.

Thanks to recent post on the official newsgroup by Oleksandr Rasputinov, now I have learned two undocumented functions which control the tolerance of Equal and SameQ: $EqualTolerance and $SameQTolerance. In Mathematica version 5 and earlier these functions live in the Experimental` context and are well documented: $EqualTolerance, $SameQTolerance. Starting from version 6, they are moved to the Internal` context and become undocumented but still work and even have built-in diagnostic messages which appear when one try to assign them illegal values:

In[1]:= Internal`$SameQTolerance = a

During evaluation of In[2]:= Internal`$SameQTolerance::tolset: 
Cannot set Internal`$SameQTolerance to a; value must be a real 
number or +/- Infinity.

Out[1]= a

Citing Oleksandr Rasputinov:

Internal`$EqualTolerance ... takes a machine real value indicating the number of decimal digits' tolerance that should be applied, i.e. Log[2]/Log[10] times the number of least significant bits one wishes to ignore.

In this way, setting Internal`$EqualTolerance to zero will force Equal to consider numbers equal only when they are identical in all binary digits (not considering out-of-Precision digits):

In[2]:= Block[{Internal`$EqualTolerance = 0}, 
           1.0000000000000021 == 1.0000000000000022]
Out[2]= False

In[5]:= Block[{Internal`$EqualTolerance = 0}, 
           1.00000000000000002 == 1.000000000000000029]
        Block[{Internal`$EqualTolerance = 0}, 
           1.000000000000000020 == 1.000000000000000029]
Out[5]= True
Out[6]= False

Note the following case:

In[3]:= Block[{Internal`$EqualTolerance = 0}, 
           1.0000000000000020 == 1.0000000000000021]
        RealDigits[1.0000000000000020, 2] === RealDigits[1.0000000000000021, 2]
Out[3]= True
Out[4]= True

In this case both numbers have MachinePrecision which effectively is

In[5]:= $MachinePrecision
Out[5]= 15.9546

(53*Log[10, 2]). With such precision these numbers are identical in all binary digits:

In[6]:= RealDigits[1.0000000000000020` $MachinePrecision, 2] === 
                   RealDigits[1.0000000000000021` $MachinePrecision, 2]
Out[6]= True

Increasing precision to 16 makes them different arbitrary-precision numbers:

In[7]:= RealDigits[1.0000000000000020`16, 2] === 
              RealDigits[1.0000000000000021`16, 2]
Out[7]= False

In[8]:= Row@First@RealDigits[1.0000000000000020`16,2]
         Row@First@RealDigits[1.0000000000000021`16,2]
Out[9]= 100000000000000000000000000000000000000000000000010010
Out[10]= 100000000000000000000000000000000000000000000000010011

But unfortunately Equal still fails to distinguish them:

In[11]:= Block[{Internal`$EqualTolerance = 0}, 
 {1.00000000000000002`16 == 1.000000000000000021`16, 
  1.00000000000000002`17 == 1.000000000000000021`17, 
  1.00000000000000002`18 == 1.000000000000000021`18}]
Out[11]= {True, True, False}

There is an infinite number of such cases:

In[12]:= Block[{Internal`$EqualTolerance = 0}, 
  Cases[Table[a = SetPrecision[1., n]; 
    b = a + 10^-n; {n, a == b, RealDigits[a, 2] === RealDigits[b, 2], 
     Order[a, b] == 0}, {n, 15, 300}], {_, True, False, _}]] // Length

Out[12]= 192

Interestingly, sometimes RealDigits returns identical digits while Order shows that internal representations of expressions are not identical:

In[13]:= Block[{Internal`$EqualTolerance = 0}, 
  Cases[Table[a = SetPrecision[1., n]; 
    b = a + 10^-n; {n, a == b, RealDigits[a, 2] === RealDigits[b, 2], 
     Order[a, b] == 0}, {n, 15, 300}], {_, _, True, False}]] // Length

Out[13]= 64

But it seems that opposite situation newer happens:

In[14]:= 
Block[{Internal`$EqualTolerance = 0}, 
  Cases[Table[a = SetPrecision[1., n]; 
    b = a + 10^-n; {n, a == b, RealDigits[a, 2] === RealDigits[b, 2], 
     Order[a, b] == 0}, {n, 15, 3000}], {_, _, False, True}]] // Length

Out[14]= 0

Try this:

realEqual[a_, b_] := SameQ @@ RealDigits[{a, b}, 2, Automatic]

The choice of base 2 is crucial to ensure that you are comparing the internal representations.

In[54]:= realEqual[1.0000000000000021, 1.0000000000000021]
Out[54]= True

In[55]:= realEqual[1.0000000000000021, 1.0000000000000022]
Out[55]= False

In[56]:= realEqual[
           1.000000000000000000000000000000000000000000000000000000000000000022
         , 1.000000000000000000000000000000000000000000000000000000000000000023
         ]
Out[56]= False

I'm not aware of an already defined operator. But you may define for example:

longEqual[x_, y_] := Block[{$MaxPrecision = 20, $MinPrecision = 20},
                            Equal[x - y, 0.]]  

Such as:

longEqual[1.00000000000000223, 1.00000000000000223]
True
longEqual[1.00000000000000223, 1.00000000000000222]
False   

Edit

If you want to generalize for an arbitrary number of digits, you can do for example:

longEqual[x_, y_] :=
 Block[{
   $MaxPrecision =  Max @@ StringLength /@ ToString /@ {x, y},
   $MinPrecision =  Max @@ StringLength /@ ToString /@ {x, y}},
   Equal[x - y, 0.]]

So that your counterexample in your comment also works.

HTH!

I propose a strategy that uses RealDigits to compare the actual digits of the numbers. The only tricky bit is stripping out trailing zeroes.

trunc = {Drop[First@#, Plus @@ First /@ {-Dimensions@First@#, 
         Last@Position[First@#, n_?(# != 0 &)]}], Last@#} &@ RealDigits@# &;
exactEqual = SameQ @@ trunc /@ {#1, #2} &;

In[1]  := exactEqual[1.000000000000000000000000000000000000000000000000000111,
                     1.000000000000000000000000000000000000000000000000000111000]
Out[1] := True
In[2]  := exactEqual[1.000000000000000000000000000000000000000000000000000111,
                     1.000000000000000000000000000000000000000000000000000112000]
Out[2] := False

I think that you really have to specify what you want... there's no way to compare approximate real numbers that will satisfy everyone in every situation.

Anyway, here's a couple more options:

In[1]:= realEqual[lhs_,rhs_,tol_:$MachineEpsilon] := 0==Chop[lhs-rhs,tol]

In[2]:= Equal[1.0000000000000021,1.0000000000000021]
        realEqual[1.0000000000000021,1.0000000000000021]
Out[2]= True
Out[3]= True

In[4]:= Equal[1.0000000000000022,1.0000000000000021]
        realEqual[1.0000000000000022,1.0000000000000021]
Out[4]= True
Out[5]= False

As the precision of both numbers gets higher, then they can always be distinguished if you set tol high enough.

Note that the subtraction is done at the precision of the lowest of the two numbers. You could make it happen at the precision of the higher number (which seems a bit pointless) by doing something like

maxEqual[lhs_, rhs_] := With[{prec = Max[Precision /@ {lhs, rhs}]}, 
  0 === Chop[SetPrecision[lhs, prec] - SetPrecision[rhs, prec], 10^-prec]]

maybe using the minimum precision makes more sense

minEqual[lhs_, rhs_] := With[{prec = Min[Precision /@ {lhs, rhs}]}, 
  0 === Chop[SetPrecision[lhs, prec] - SetPrecision[rhs, prec], 10^-prec]]

One other way to define such function is by using SetPrecision:

MyEqual[a_, b_] := SetPrecision[a, Precision[a] + 3] == SetPrecision[b, Precision[b] + 3]

This seems to work in the all cases but I'm still wondering is there a built-in function. It is ugly to use high-level functions for such a primitive task...

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!