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 最近在官方新闻组上发表的文章,现在我已经学会了两个无记录的功能来控制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
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)
试试这个:
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)
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的精度不同,因此它们不会被视为相同.
我不知道已经定义的运算符。但是您可以定义例如:
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!
我提出了一种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)
归档时间: |
|
查看次数: |
816 次 |
最近记录: |