感谢最近由 Oleksandr Rasputinov 在官方新闻组上发布的post,现在我学习了两个未记录的函数,它们控制Equal 和SameQ 的容差:$EqualTolerance 和$SameQTolerance。在 Mathematica 版本 5 和更早的版本中,这些函数存在于 Experimental` 上下文中,并且有据可查:$EqualTolerance、$SameQTolerance。从版本 6 开始,它们是 moved to the Internal` context 并且没有记录,但仍然可以工作,甚至具有内置的诊断消息,当人们尝试为其分配非法值时会出现这些消息:
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, _}]] // 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
但似乎新的相反情况发生了:
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