11

Below is a pure function f for which f(a) !== f(b) despite a === b (notice the strict equalities) for some values of a and b:

var f = function (x) {
   return 1 / x;
}

+0 === -0 // true
f(+0) === f(-0) // false

The existence of such functions can lead to difficult-to-find bugs. Are there other examples I should be weary of?

Randomblue
  • 112,777
  • 145
  • 353
  • 547
  • 1
    Is this a programming problem? I'm not sure I quite understand the question. – Gabe Aug 28 '11 at 20:14
  • @Gabe: Well _strict_ equality `===`, as the name suggests, should be _strict_. The function I have provided shows that strict equality is actually not so strict, which is indeed a problem for me. – Randomblue Aug 28 '11 at 20:16
  • @Random: Strictly equality only means that **no type conversion** will be performed. `-0` and `+0` are both numbers. Why would you ever want to distinguish between them anyway? Division by zero is not defined anyway, other languages throw an error in this case. – Felix Kling Aug 28 '11 at 21:22
  • Because the property `a === b` implies `f(a) === f(b)` for pure functions `f` is a nice one to have, don't you think so? – Randomblue Aug 28 '11 at 21:25
  • In general maybe. But in JavaScript, if you define `+0 !== -0`, then this implies that `Math.round(-5 / 10) !== 0`. And this is probably a much more common situation and and would create much more problems. You also have to consider the practical aspects, not only the theoretical ones. – Felix Kling Aug 28 '11 at 21:30
  • The right answer might be to use/find/create means of dealing with "exceptional" conditions in IEEE-754 arithmetic (http://en.wikipedia.org/wiki/Floating_point#Dealing_with_exceptional_cases) – minopret Aug 03 '13 at 17:08

6 Answers6

11

Yes, because NaN !== NaN.

var f = function (x) { return Infinity - x; }

Infinity === Infinity // true
f(Infinity) === f(Infinity) // false

f(Infinity) // NaN

Some other examples that yield NaN whose arguments can be strictly equal:

0/0
Infinity/Infinity
Infinity*0
Math.sqrt(-1)
Math.log(-1)
Math.asin(-2)
Casey Chu
  • 25,069
  • 10
  • 40
  • 59
6

this behaviour is perfectly ok, because, in mathematical theory, -0 === +0 is true, and 1/(-0) === 1/(+0) is not, because -inf != +inf

EDIT: although I am really surprised that javascript can in fact handle these kinds of mathematical concepts.

EDIT2: additionally, the phenomenon you described is completely based on the fact, that you divide by zero from which you should expect at least some strange behaviour.

Andreas Grapentin
  • 5,499
  • 4
  • 39
  • 57
  • I know the explanation for this example. I'm interested in _other_ examples. – Randomblue Aug 28 '11 at 20:07
  • well, I can think of a bunch of functions, where `f(-0) !== f(+0)`, but imo javascript should evaluate `-0 === +0` to false in the first place. Don't think there will be any examples not based on `-0 === +0` – Andreas Grapentin Aug 28 '11 at 20:10
  • Can you 'prove' that there are no other examples? – Randomblue Aug 28 '11 at 20:17
  • how could I? a proof would have to contain the entire javascript language specification. – Andreas Grapentin Aug 28 '11 at 20:31
  • I know. Maybe you had some sort of heuristic or justification? – Randomblue Aug 28 '11 at 20:32
  • 1
    rule of thumb. `-0 === +0` does only evaluate to true because of complex mathematical backgrounds, of which I don't know enough to translate them to understandable english. Afaik, this is the only base, for which `f(x) !== f(y)` while `x === y`. – Andreas Grapentin Aug 28 '11 at 20:36
  • @Andreas: FWIWI, the equality is in the specification. See the OPs previous question: http://stackoverflow.com/questions/7223359/0-and-0-in-javascript-negative-zero-and-positive-zero-in-javascript/7223395 – Felix Kling Aug 28 '11 at 21:20
  • @Andreas: See the example I have accepted for a different example from mine. – Randomblue Aug 29 '11 at 05:49
2

In ECMAScript 3, another example where === behaves surprisingly is with joined functions. Consider a case like this:

function createConstantFunction(result) {
    return function () {
        return result;
    };
}

var oneReturner = createConstantFunction(1);  // a function that always returns 1
var twoReturner = createConstantFunction(2);  // a function that always returns 2

An implementation is allowed to "join" the two functions (see §13.2 of the spec), and if it does so, then oneReturner === twoReturner will be true (see §13.1.2), even though the two functions do different things. Similarly with these:

// a perfect forwarder: returns a sort of "duplicate" of its argument
function duplicateFunction(f) {
    return function (f) {
        return f.apply(this, arguments);
    };
}

var myAlert = duplicateFunction(alert);
console.myLog = duplicateFunction(console.log);

Here an implementation can say that myAlert === console.myLog, even though myAlert is actually equivalent to alert and console.myLog is actually equivalent to console.log.

(However, this aspect of ECMAScript 3 was not preserved in ECMAScript 5: functions are no longer allowed to be joined.)

ruakh
  • 175,680
  • 26
  • 273
  • 307
  • Can you add a JSfiddle with an example? – Randomblue May 31 '13 at 22:43
  • @Randomblue: I could, but it wouldn't work in any browser that supports ECMAScript 5 (which no longer allows this behavior), and wouldn't necessarily work even in older browsers (since this behavior was only *allowed*, not *required*). So I think a Fiddle would be likely to confuse more people than it helped. – ruakh May 31 '13 at 22:46
2

1/+0 is Infinity and 1/-0 -Infinity, while +0 === -0.

This can be explained by the fact that ECMA defines -0 to equal +0 as a special case, while in other operations these two values retain their different properties, which result in some inconsistencies.

This is only possible because the language explicitly defines two non-equal values to be equal, that in fact are not.

Other examples, if any, should be based on the same sort of artificial equality, and given http://ecma262-5.com/ELS5_HTML.htm#Section_11.9.6 there is no other such excention, so probably no other example of this.

If it's of any use, we can ensure that 0 is not -0 by adding 0 to it:

var f = function(x) {
   return 1 / (x + 0);
}
f(+0) === f(-0)
Arnaud Le Blanc
  • 98,321
  • 23
  • 206
  • 194
  • This example is essentially the same as mine. Do you have one which is not trivially different from mine? – Randomblue Aug 28 '11 at 20:18
  • I originally though the question was *why f(+0) !== f(-0)*; I've updated the answer. – Arnaud Le Blanc Aug 28 '11 at 20:28
  • Given that this counter-intuitive case is due to the artificial technical statement that the `===` operator must return true when passed the two different values -0 and +0, if there is no other exception like this, there is probably no other case like this. The only way of producing an other case like this would be to declare that the === operator return true if passed the values 1 and 2. Or that two equal values are different, like NaN, in fact. – Arnaud Le Blanc Aug 28 '11 at 20:47
  • Can you make such a declaration?! – Randomblue Aug 28 '11 at 20:48
  • there is probably / the only way would – Arnaud Le Blanc Aug 28 '11 at 20:50
0

I'm not so sure this is so scary ;-) Javascript is not a pure language and the presence of +/-0 and the equality of -0 and +0 are specific to IEEE-754 and are "well defined", even if perhaps sometimes surprising. (Even NaN != NaN always being true is well defined, for instance.)

From signed zero:

According to the IEEE 754 standard, negative zero and positive zero should compare as equal with the usual (numerical) comparison operators...

Technically, because the two inputs to f are different, then the result can also be different. For what it's worth, Haskell will treat 0 == -0 as true but will treat (1 / 0) == (1 / (-0)) as false.

However, I do find this an interesting question.

Happy coding.

  • So are there ways of distinguishing `+0` from `-0` using something else than my infinity trick? Should maybe consider augmenting the language to include `====` where `+0 ==== -0` is false. – Randomblue Aug 28 '11 at 20:24
-1

There are many such functions, here is another example

function f (a) {
  return a + 1;
}

1 == "1" but f(1) != f("1")

This is because equality is a nuanced concept.

Perhaps more scary is that in your example -0 === +0.

HBP
  • 15,685
  • 6
  • 28
  • 34
  • 2
    This uses the `==` operator. Using the `===` operator brings it in-line. –  Aug 28 '11 at 20:12