Is there a “normal” EqualQ function in Mathematica?

后端 未结 7 575
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-24 03:49

On the documentation page for Equal we read that

Approximate numbers with machine precision or higher are considered equal if they d

相关标签:
7条回答
  • 2020-12-24 04:13

    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
    
    0 讨论(0)
  • 2020-12-24 04:28
    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.

    0 讨论(0)
  • 2020-12-24 04:29

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

    0 讨论(0)
  • 2020-12-24 04:32

    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
    
    0 讨论(0)
  • 2020-12-24 04:35

    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!

    0 讨论(0)
  • 2020-12-24 04:38

    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]]
    
    0 讨论(0)
提交回复
热议问题