在Equal
的文档页面上,我们读到了
机器的近似数字 考虑精度或更高 如果他们最多不同,那就相等 最后七位二进制数字(粗略表示 他们的最后两位小数)。
以下是示例(32位系统;对于64位系统,在中间添加一些零):
In[1]:= 1.0000000000000021 == 1.0000000000000022
1.0000000000000021 === 1.0000000000000022
Out[1]= True
Out[2]= True
我想知道 Mathematica 中Equal
函数的“正常”模拟是否会丢弃最后7个二进制数字?
答案 0 :(得分:14)
感谢Oleksandr Rasputinov官方新闻组最近的post,现在我学会了两个无记录的函数来控制Equal
和SameQ
:$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]
这似乎适用于所有情况,但我仍然想知道是否有内置函数。将高级函数用于这样一个原始任务是很丑陋的......