感谢 Oleksandr Rasputinov 在官方新闻组上的最近 帖子,现在我学会了两个控制Equal
和SameQ
的公差的未公开函数:$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
引用Oleksandr Rasputinov的话:
Internal`$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
请注意以下情况:
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]
). 在这样的精度下,这些数字在所有二进制位上都是相同的:
In[6]:= RealDigits[1.0000000000000020` $MachinePrecision, 2] ===
RealDigits[1.0000000000000021` $MachinePrecision, 2]
Out[6]= True
将精度提高到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
但不幸的是,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, _}]]
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
但似乎相反的情况从未发生过:
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}]]
Out[14]= 0
SameQ
可以吗?也许在截断到您想要保留的数字位数后使用。 - Simon1.00000000000000000022 === 1.00000000000000000021
,你会发现它不可以。:( - Alexey Popkov100 === 1.00000000000000000021
100。 - Yaroslav BulatovSameQ
帮助文档指出它会忽略Real
对象的最后一位二进制数字。 - Yaroslav Bulatov===
会丢失数字的原因--http://thenumericalalgorithmsgroup.blogspot.com/2011/02/wandering-precision.html - Yaroslav Bulatov