Is there a “normal” EqualQ function in Mathematica

2020-02-17 10:45发布

On the documentation page for Equal we read that

Approximate numbers with machine precision or higher are considered equal if they differ in at most their last seven binary digits (roughly their last two decimal digits).

Here are examples (32 bit system; for 64 bit system add some more zeros in the middle):

In[1]:= 1.0000000000000021 == 1.0000000000000022
1.0000000000000021 === 1.0000000000000022

Out[1]= True

Out[2]= True

I'm wondering is there a "normal" analog of the Equal function in Mathematica that does not drop last 7 binary digits?

7条回答
神经病院院长
2楼-- · 2020-02-17 10:47
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.

查看更多
劫难
3楼-- · 2020-02-17 10:48

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
查看更多
Bombasti
4楼-- · 2020-02-17 10:53

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
查看更多
爷、活的狠高调
5楼-- · 2020-02-17 10:53

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...

查看更多
手持菜刀,她持情操
6楼-- · 2020-02-17 10:54

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!

查看更多
混吃等死
7楼-- · 2020-02-17 10:58

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]]
查看更多
登录 后发表回答