有没有“正常”的情况? Mathematica 中的 EqualQ 函数?

发布于 2024-10-17 04:55:32 字数 408 浏览 1 评论 0原文

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

机器的大概数字 考虑精度或更高 如果他们至多有差异,则相等 最后七个二进制数字(大约 最后两位小数)。

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

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

Out[1]= True

Out[2]= True

我想知道 Mathematica 中是否存在 Equal 函数的“正常”模拟 em> 不会删除最后 7 个二进制数字?

On the documentation page for Equal we read that

Approximate numbers with machine
precision or higher are considered
equal if they differ in at most their
last seven binary digits (roughly
their last two decimal digits).

Here are examples (32 bit system; for 64 bit system add some more zeros in the middle):

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

Out[1]= True

Out[2]= True

I'm wondering is there a "normal" analog of the Equal function in Mathematica that does not drop last 7 binary digits?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

怎樣才叫好 2024-10-24 04:55:32

感谢最近在官方新闻组上帖子 Oleksandr Rasputinov,现在我学会了两个未记录的函数,它们控制 EqualSameQ 的容差:$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:

内部`$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

Thanks to recent post on the official newsgroup by Oleksandr Rasputinov, now I have learned two undocumented functions which control the tolerance of Equal and SameQ: $EqualTolerance and $SameQTolerance. In Mathematica version 5 and earlier these functions live in the Experimental` context and are well documented: $EqualTolerance, $SameQTolerance. Starting from version 6, they are moved to the Internal` context and become undocumented but still work and even have built-in diagnostic messages which appear when one try to assign them illegal values:

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

Citing Oleksandr Rasputinov:

Internal`$EqualTolerance ... takes a
machine real value indicating the
number of decimal digits' tolerance
that should be applied, i.e.
Log[2]/Log[10] times the number of
least significant bits one wishes to
ignore.

In this way, setting Internal`$EqualTolerance to zero will force Equal to consider numbers equal only when they are identical in all binary digits (not considering out-of-Precision digits):

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

Note the following case:

In[3]:= Block[{Internal`$EqualTolerance = 0}, 
           1.0000000000000020 == 1.0000000000000021]
        RealDigits[1.0000000000000020, 2] === RealDigits[1.0000000000000021, 2]
Out[3]= True
Out[4]= True

In this case both numbers have MachinePrecision which effectively is

In[5]:= $MachinePrecision
Out[5]= 15.9546

(53*Log[10, 2]). With such precision these numbers are identical in all binary digits:

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

Increasing precision to 16 makes them different arbitrary-precision numbers:

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

But unfortunately Equal still fails to distinguish them:

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}

There is an infinite number of such cases:

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

Interestingly, sometimes RealDigits returns identical digits while Order shows that internal representations of expressions are not identical:

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

But it seems that opposite situation newer happens:

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
北斗星光 2024-10-24 04:55:32

试试这个:

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

Try this:

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

The choice of base 2 is crucial to ensure that you are comparing the internal representations.

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
少女净妖师 2024-10-24 04:55:32
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 的精度不同,因此它们不会被视为相同。

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

This tests if two object are identical, since 1.0000000000000021 and 1.000000000000002100 differs in precision they won't be considered as identical.

雨后咖啡店 2024-10-24 04:55:32

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

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.]]

这样您评论中的反例也有效。

哈!

I'm not aware of an already defined operator. But you may define for example:

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

Such as:

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

Edit

If you want to generalize for an arbitrary number of digits, you can do for example:

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

So that your counterexample in your comment also works.

HTH!

别想她 2024-10-24 04:55:32

我提出了一种使用 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

I propose a strategy that uses RealDigits to compare the actual digits of the numbers. The only tricky bit is stripping out trailing zeroes.

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
神经大条 2024-10-24 04:55:32

我认为你真的必须明确你想要什么......没有办法比较近似的实数来满足每个人在每种情况下的需求。

无论如何,这里还有几个选项:

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]]

I think that you really have to specify what you want... there's no way to compare approximate real numbers that will satisfy everyone in every situation.

Anyway, here's a couple more options:

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

As the precision of both numbers gets higher, then they can always be distinguished if you set tol high enough.

Note that the subtraction is done at the precision of the lowest of the two numbers. You could make it happen at the precision of the higher number (which seems a bit pointless) by doing something like

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

maybe using the minimum precision makes more sense

minEqual[lhs_, rhs_] := With[{prec = Min[Precision /@ {lhs, rhs}]}, 
  0 === Chop[SetPrecision[lhs, prec] - SetPrecision[rhs, prec], 10^-prec]]
幸福不弃 2024-10-24 04:55:32

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

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

这似乎适用于所有情况,但我仍然想知道是否有内置函数。使用高级函数来完成如此原始的任务是很丑陋的......

One other way to define such function is by using SetPrecision:

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

This seems to work in the all cases but I'm still wondering is there a built-in function. It is ugly to use high-level functions for such a primitive task...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文