Mathematica中是否存在"正常"的EqualQ功能?

Ale*_*kov 10 wolfram-mathematica

Equal我们阅读的文档页面上

如果机器精度或更高的近似数字最多与它们的最后七位二进制数字(大致是它们的最后两位小数位)不同,则认为它们是相等的.

以下是示例(32位系统;对于64位系统,在中间添加一些零):

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

Out[1]= True

Out[2]= True
Run Code Online (Sandbox Code Playgroud)

我想知道Mathematica中的Equal函数的"正常"模拟是否会丢弃最后7个二进制数字?

Ale*_*kov 16

感谢Oleksandr Rasputinov 最近在官方新闻组上发表的文章,现在我已经学会了两个无记录的功能来控制EqualSameQ:$EqualTolerance$SameQTolerance.在Mathematica第5版及更早版本中,这些函数存在于Experimental`上下文中并且有详细记录:$ EqualTolerance, $ SameQTolerance.从版本6开始,它们被移动到Internal`上下文并变为未记录但仍然有效,甚至还有内置诊断消息,当尝试为它们分配非法值时出现这些消息:

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
Run Code Online (Sandbox Code Playgroud)

引用Oleksandr Rasputinov:

内部`$ EqualTolerance ...取机器实数值,表示应该应用的小数位数的容差,即Log [2]/Log [10]乘以一个人想要忽略的最低有效位数.

这样,设置Internal`$EqualTolerance为零将强制Equal仅在所有二进制数字相同时才考虑数字相等(不考虑超出Precision数字):

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
Run Code Online (Sandbox Code Playgroud)

请注意以下情况:

In[3]:= Block[{Internal`$EqualTolerance = 0}, 
           1.0000000000000020 == 1.0000000000000021]
        RealDigits[1.0000000000000020, 2] === RealDigits[1.0000000000000021, 2]
Out[3]= True
Out[4]= True
Run Code Online (Sandbox Code Playgroud)

在这种情况下,两个数字都MachinePrecision有效

In[5]:= $MachinePrecision
Out[5]= 15.9546
Run Code Online (Sandbox Code Playgroud)

(53*Log[10, 2]).如此精确,这些数字在所有二进制数字中都是相同的:

In[6]:= RealDigits[1.0000000000000020` $MachinePrecision, 2] === 
                   RealDigits[1.0000000000000021` $MachinePrecision, 2]
Out[6]= True
Run Code Online (Sandbox Code Playgroud)

将精度提高到16会使它们具有不同的任意精度数:

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
Run Code Online (Sandbox Code Playgroud)

但不幸的是Equal仍然无法区分它们:

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}
Run Code Online (Sandbox Code Playgroud)

有无数个这样的情况:

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
Run Code Online (Sandbox Code Playgroud)

有趣的是,有时RealDigits返回相同的数字,同时Order表明表达式的内部表示不相同:

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
Run Code Online (Sandbox Code Playgroud)

但似乎相反的情况更新了:

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
Run Code Online (Sandbox Code Playgroud)


WRe*_*ach 8

试试这个:

realEqual[a_, b_] := SameQ @@ RealDigits[{a, b}, 2, Automatic]
Run Code Online (Sandbox Code Playgroud)

基数2的选择对于确保您比较内部表示至关重要.

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
Run Code Online (Sandbox Code Playgroud)


ken*_*ytm 6

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
Run Code Online (Sandbox Code Playgroud)

这将测试两个对象是否相同,因为1.0000000000000021和1.000000000000002100的精度不同,因此它们不会被视为相同.


Dr.*_*ius 5

我不知道已经定义的运算符。但是您可以定义例如:

longEqual[x_, y_] := Block[{$MaxPrecision = 20, $MinPrecision = 20},
                            Equal[x - y, 0.]]  
Run Code Online (Sandbox Code Playgroud)

如:

longEqual[1.00000000000000223, 1.00000000000000223]
True
longEqual[1.00000000000000223, 1.00000000000000222]
False   
Run Code Online (Sandbox Code Playgroud)

编辑

如果要归纳任意数量的数字,可以执行例如:

longEqual[x_, y_] :=
 Block[{
   $MaxPrecision =  Max @@ StringLength /@ ToString /@ {x, y},
   $MinPrecision =  Max @@ StringLength /@ ToString /@ {x, y}},
   Equal[x - y, 0.]]
Run Code Online (Sandbox Code Playgroud)

这样您的评论中的反例也可以使用。

HTH!


Tim*_*imo 5

我提出了一种RealDigits用于比较数字的实际数字的策略。唯一棘手的问题是去除尾随零。

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
Run Code Online (Sandbox Code Playgroud)