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

时间:2011-02-13 11:36:26

标签: wolfram-mathematica

Equal的文档页面上,我们读到了

  

机器的近似数字   考虑精度或更高   如果他们最多不同,那就相等   最后七位二进制数字(粗略表示   他们的最后两位小数)。

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

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

Out[1]= True

Out[2]= True

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

7 个答案:

答案 0 :(得分:14)

感谢Oleksandr Rasputinov官方新闻组最近的post,现在我学会了两个无记录的函数来控制EqualSameQ$EqualTolerance的容忍度和{ {1}}。在 Mathematica 第5版及更早版本中,这些函数位于$SameQTolerance上下文中,并且有详细记录:$EqualTolerance$SameQTolerance。从版本6开始,它们是moved to the Internal` context并且没有文档但仍然有效,甚至还有内置的诊断消息,这些消息在尝试为它们分配非法值时出现:

Experimental`

引用Oleksandr Rasputinov:

  

内部`$ EqualTolerance ...需要一个   机器实际值表示   小数位数的容差   那应该适用,即   记录[2] / Log [10]次数   一个人希望得到的最低有效位   忽略。

通过这种方式,将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 设置为零将强制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

在这种情况下,这两个数字都有In[3]:= Block[{Internal`$EqualTolerance = 0}, 1.0000000000000020 == 1.0000000000000021] RealDigits[1.0000000000000020, 2] === RealDigits[1.0000000000000021, 2] Out[3]= True Out[4]= True ,实际上是

MachinePrecision

In[5]:= $MachinePrecision Out[5]= 15.9546 )。如此精确,这些数字在所有二进制数字中都是相同的:

53*Log[10, 2]

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

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

但不幸的是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 仍无法区分它们:

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}

有趣的是,有时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 返回相同的数字,而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

答案 1 :(得分: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

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

答案 2 :(得分:6)

试试这个:

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

选择基数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

答案 3 :(得分:4)

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

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

如:

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

修改

如果要概括任意数量的数字,可以这样做:

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

因此,您的评论中的反例也有效。

HTH!

答案 4 :(得分:4)

我建议使用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

答案 5 :(得分:2)

我认为你真的必须指明你想要的东西......没有办法比较在每种情况下都能满足每个人的近似实数。

无论如何,这里有更多选择:

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

随着两个数字的精度变高,如果将tol设置得足够高,则可以始终区分它们。

请注意,减法是以两个数中最低的精度完成的。你可以通过做类似

之类的事情,以较高数字的精度(这似乎有点无意义)来实现
maxEqual[lhs_, rhs_] := With[{prec = Max[Precision /@ {lhs, rhs}]}, 
  0 === Chop[SetPrecision[lhs, prec] - SetPrecision[rhs, prec], 10^-prec]]

可能使用最小精度更有意义

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

答案 6 :(得分:1)

定义此类函数的另一种方法是使用SetPrecision:

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

这似乎适用于所有情况,但我仍然想知道是否有内置函数。将高级函数用于这样一个原始任务是很丑陋的......