Let's define two different numbers.
x = 1.
y = 1. + 2^-52 (* equivalently, 1 + $MachineEpsilon *)
Let's make sure they're different with FullForm
:
x // FullForm (* 1.` *)
y // FullForm (* 1.0000000000000002` *)
Those look pretty close... let's make sure they're different. I'm not a wizard with the developer tools, but I can export them as IEEE double-precision floating point numbers (which I'd bet is their internal representation):
StringJoin @@
IntegerString[Reverse@ToCharacterCode[ExportString[x, "Real64"]],
16, 2]
(* 3ff0000000000000 *)
StringJoin @@
IntegerString[Reverse@ToCharacterCode[ExportString[y, "Real64"]],
16, 2]
(* 3ff0000000000001 *)
We can see that they are indeed different. They represent the two numbers:
$$ \begin{align} x &= (1.){\underbrace{000 \cdots 000}_\text{51 zeros}}0_2 \times 2^{01111111111_2 - 1023} \equiv 1 \\ y &= (1.){\underbrace{000 \cdots 000}_\text{51 zeros}}1_2 \times 2^{01111111111_2 - 1023} \equiv 1 + \frac{1}{2^{52}} \end{align} $$
That is, x
is exactly one, and y
is the smallest IEEE double greater than one. Ok, so they're different. Hey Mathematica, you know they're diff-
x == y (* True *)
Oh. What if we try-
x === y (* True *)
Hey Python, you use doubles, right? Are you seeing this?
>>> 1. == 1.0000000000000002
False
Maybe it's because you're using quads?
>>> 1. == 1.0000000000000001
True
Yeah, I didn't think so. Mathematica, are you sure? I mean, this doesn't seem right...
y - x (* 2.22045*10^-16 *)
Aha! I knew it! Now let's try this:
y - x == 0 (* False *)
Success! Now let's just double-check (pun intended):
1.0000000000000001 - 1. (* 0. *)
% == 0 (* True *)
So you are using double-precision...
My question is, Why do Equal
and SameQ
return True
, even though these numbers are obviously different? SameQ
ignores the last bit, and Equal
ignores the last seven bits!
Answer
It seems I found my answer in OleksandrR's comment to this question. He says,
Bear in mind
Equal
applies an extra tolerance in Mathematica. The proper comparison isBlock[{Internal`$EqualTolerance = -Infinity}, 1 == 1 + $MachineEpsilon] (* False *)
Block[{Internal`$EqualTolerance = -Infinity}, 1 == 1 + $MachineEpsilon/2] (* True *)
In fact, the value of Internal`$EqualTolerance * Log2[10]
is 7.
, meaning that it ignores the last seven bits, just as I discovered!
(Analogously, Internal`$SameQTolerance * Log2[10]
is 1.
, i.e. it drops the last bit.)
Note that this is mentioned in the documentation for Equal
, under Details:
- 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).
- For numbers below machine precision the required tolerance is reduced in proportion to the precision of the numbers.
However, I never thought to look at it, since (thought) I knew what ==
means! Lesson learned, always check the documentation, especially if you don't think you need to.
Comments
Post a Comment