Identifying Negative Zero
Tags: javascript, wtfjs, negative, ieee
Published: 2020-01-25 01:19:40

As usual, I like to delve into some of the weird or complicated bits of JS. The other week I was thinking about a way to possibly identify a  negative zero.

Some of you may be taken aback. What is a negative zero? Can zero even be negative? It can according to the [IEEE](https://en.wikipedia.org/wiki/Signed_zero)! This is a standard used for almost all languages in their number systems. Thusly, a lot of the "weirdness" in JS number systems is actually standard across other languages, too!

Although, how would we know if the input we were receiving was `-0` or `0`? Does it matter? Not really, but it's a fun exercise.

Let's first create the rules. We have a function called `isZeroNegative` which accepts a single parameter. We must return `true` if the zero is negative and `false` if it is a positive zero. We can write whatever code we want inside the function.

```js
function isZeroNegative(zero) {
  // some code
  return false; // default return
}
```

## Our Options

It seems pretty simple, but you will find it a bit challenging. You might first think to check if it's below `0`, as that's usually the definition of a negative number, but this won't do. The check will return `false` even though it is negative. `-0 < 0 === false`. 

`Math.sign()` might be your next guess. Its very purpose, after all, is to let us know whether a number is negative or not. If you pass a negative number, it will return `-1` and `1` if it is positive. Unfortunately, it will return the same zero we passed in if the number is a zero, whether negative or positive. `Math.sign(-0) === -0`.

The next option might be to just check if it's strictly equal to `-0`. Easy enough! Unfortunately, not even strict equality is strict enough! `-0 === 0`.

We're running out of options quickly. We need to run our `-0` through a system that won't spit out a zero. It needs to give us back a number lower or higher than that or just a different data-type altogether.

What about stringification? We can call `.toString()` on any number to get its string equivalent. We can then check for the negative sign. We can get the negative sign in `-4` by doing `(-4).toString()[0]`. Unfortunately, yet again, negative zero is one step ahead of us. Darn that negative zero!

Doing `(-0).toString()` will simply result in `"0"`. Negative zero stringifies to just zero. 

Enough of the cat-and-mouse games. Let's review some *actual* answers. Try to think of one your own. Remember what we said 3 paragraphs back.

## Actual Answers

There are a couple of methods. These might not even be an exhaustive list, so feel free to try out other ideas!

I first came across [this blog post](http://www.wirfs-brock.com/allen/posts/128) when researching this problem. It's from Allen Wirfs-Brock, a person currently on tc39, but it was was written in 2011, so there may be more methods made available recently. I will be taking the first 2 solutions from there.

The first solution we're going to explore is possibly the most performant as we don't change any data types. We work entirely within the number system.

### Mathematics and the IEEE

We should first think about what kind of mathematical application can we involve `-0` in to affect the result? Addition or subtraction? No, those would both act similarly to `0`. Multiplication or Division? Multiplication would give us a zero (either negative or positive) and so that leaves us right where we started. Division with the zero as the numerator brings the same problem as multiplication!

What if we involve `-0` as the denominator? Any number aside for another zero on the numerator would result in `-Infinity`! We can check for `-Infinity` pretty easily!

We have to be sure it is only `-0` on the denominator that can result in `-Infinity`, however. [Jeff Waldon](http://whereswalden.com/) provides `-Math.pow(2, -1074)` as a counterexample. We can thusly add a strictly equals check to ensure we're dealing with a zero.

Via this solution, our winning code is now.
```js
function isZeroNegative(zero) {
  const isZero = zero === 0;
  const isNegative = 1 / zero === -Infinity;
  return isNegative && isZero;
}
```

Interestingly enough, we can now create a more full-proof `Math.sign()` using the same sort of method. This one works the same as the old one, but now works well with zeros. As a side-effect, `Math.sign('')` now returns `1` instead of `0`.

```js
Math.sign = function(num) {
    if (Number(num) !== Number(num)) return NaN;
    if (num === -Infinity) return -1;
    return 1 / num < 0 ? -1 : 1;
}
```


### The Strictest Equality

Let's go back to a previous attempt at a solution. We found that strict equality (`===`) wasn't strict enough. What kind of equality is more strict than that? What about constants? If the engine can identify `-0` as being different than `0`, we may be able to use that somehow.

Unfortunately, `const` is **too** strict. We can't reassign a constant variable in the same scope, no matter what. Even if we reassign it to what it already was. The mere presence of the `=` operator is enough to trigger an error.

What we're trying to do is something like the following.
```js
function isZeroNegative(zero) {
  if (zero !== 0) return false;

  const posZero = 0;
  try {
    posZero = num;
  } catch(e) {
    return true;
  }

  return false;
}
```
This will, unfortunately, trip even if we receive a positive zero. As mentioned before, the presence of `=` is enough to set things off.

Are there any other constants in JS? Well there in fact are! Using `Object.freeze`, we can make an object immutable.

It is important to note that mutating a property on an object that has been frozen will not throw an error. We need that to happen. To do so, we will use the more direct `Object.defineProperty`.

```js
function isZeroNegative(zero) {
  if (zero !== 0) return false;

  const posZero = Object.freeze({ val: 0 });
  try {
    Object.defineProperty(posZero, 'val', { value: num });
  } catch(e) {
    return true;
  }

  return false;
}
```

This will throw an error if the new value is anything other than `0`!

### Modern String Conversion

Let's yet again approach a solution we dismissed earlier. While it is true that `(-0).toString()` returns `"0"`, there is a more modern stringifier - `.toLocaleString()`. It's pretty powerful, but recently I came across [a tweet](https://twitter.com/ManuelBieh/status/1220664413037965312) regarding how calling it on `Infinity` will return the symbol ("∞"), not the normal stringified version ("Infinity").

Calling `.toLocaleString()` on `-0` actually returns `"-0"`! 
Using this finding, we can modify the code as follows:

```js
function isZeroNegative(zero) {
  if (zero !== 0) return false;

  return zero.toLocaleString()[0] === "-";
}
```

The last one is the quickest and simplest, but possibly not the most performant, which may be important in situations where finding a negative zero is important.

# Conclusion

Why would one even need to find a negative zero? I can't think of one off hand. The reason isn't necessary.

This article was more so an investigation into thought processes. Solving problems is an extremely important skill as a software engineer. The way you investigate solutions can be more important than the solution itself.