-3

When dealing with floating-point numbers, −0 is a different number from 0. I am building a timer application in JavaScript and apparently found out the hard way when converting the number of minutes into a string.

For any other number of minutes than −0, it works just fine to do this:

var minutes = (time_balance_s/60);
minutes = Math.sign(minutes) * Math.floor(Math.abs(minutes));
var seconds = time_balance_s - (minutes * 60);
seconds = Math.floor(Math.abs(seconds));
displayString = minutes+":"+seconds.toString().padStart(2, '0');

But for −0, the minus sign is omitted, making the case where we are within a minute past our time indistinguishable from the case where we have less than a minute left. (i.e. if there are 21 seconds left, or if we are 21 seconds overtime, the time shows "0:21")

I initially thought it was something to do with order or operations and "+" being ambiguous in JavaScript. But even this won't work:

var minutes = (time_balance_s/60);
minutes = Math.sign(minutes) * Math.floor(Math.abs(minutes));
var seconds = time_balance_s - (minutes * 60);
seconds = Math.floor(Math.abs(seconds));
displayString = minutes.toString()+":"+seconds.toString().padStart(2, '0');

After a lot of searching, I found I had to make a special case to handle this, and use a special Object.is operation. This is frustrating.

var minutes = (time_balance_s/60);
minutes = Math.sign(minutes) * Math.floor(Math.abs(minutes));
var seconds = time_balance_s - (minutes * 60);
seconds = Math.floor(Math.abs(seconds));
if (Object.is(minutes, -0)) {
  displayString = "-0"+":"+seconds.toString().padStart(2, '0');
} else {
  displayString = minutes.toString()+":"+seconds.toString().padStart(2, '0');
}

Mozilla developer docs suggest this is deliberate:

Both 0 and -0 have "0" as their string representation. Infinity returns "Infinity" and NaN returns "NaN".

And I finally (well after a while) did find that someone else was asking about this behaviour and also a link to a standard. The solution of util.inspect won't work since this is browser JavaScript not Node.js JavaScript. The standard seems to explain how numbers should be handled but doesn't seem to explain why they are handled that way.

I'm wondering why JavaScript is like this. Why doesn't −0 become "-0" when converting it to a string? Is there a historic reason for this? Was there much discussion about it?

I know it's a standard now and likely wouldn't be changed, but it seems strange to me and I was curious if anyone knew. Is there some advantage case where having −0 become "0" is beneficial? Why didn't more people fight for −0 to be "-0" when the standard was being put together?

user3840170
  • 26,597
  • 4
  • 30
  • 62
azoundria
  • 940
  • 1
  • 8
  • 24
  • "*I had to use a special Object.is operator*" - I'd recommend to just use `time_balance < 0` – Bergi May 07 '23 at 20:41
  • @Bergi: I don’t think it quite answers *“I'm wondering why JavaScript is like this. Why doesn't -0 retain as "-0" when converting into a string? Is there a historic reason for this? Was there much discussion about it?”*, but okay, none of the existing ones do either. – Ry- May 07 '23 at 21:03
  • @Ry- I doubt that this is objectively answerable at all, unless someone can dig such a discussion or a statement by Brendan Eich about it - your answer at least provides a subjective (and imo correct) reason. Interestingly, it appears to have been a conscious decision and *not* inherited from Java, [whose `Double.toString` method returns `"-0.0"` for negative zero](https://docs.oracle.com/javase/8/docs/api/java/lang/Double.html#toString-double-). – Bergi May 07 '23 at 23:05
  • 1
    It might even have been a mistake, the only thing I can find is that ["*If `m` is +0 or −0, return the string `"0"`.*" could already be found in ES1](https://www.ecma-international.org/wp-content/uploads/ECMA-262_1st_edition_june_1997.pdf) and that there is [resistance to change it](https://esdiscuss.org/topic/object-is#content-17) (as usual, due to backwards compatibility) – Bergi May 07 '23 at 23:09

1 Answers1

0

I think most people would expect [0 * x === 0 => 0 * x behaves identically to 0] and want to think about the subtleties of floating point as little as possible – in every language, but maybe even especially in JavaScript, where there is no separate integer type.

As for your code, instead of multiplying by Math.sign, consider getting the sign as a string:

var sign = time_balance_s < 0 ? "-" : "";
var totalSecondsD = Math.floor(Math.abs(time_balance_s));
var minutes = Math.floor(totalSecondsD / 60);
var seconds = totalSecondsD % 60;
displayString = sign+minutes+":"+String(seconds).padStart(2, '0');
Ry-
  • 218,210
  • 55
  • 464
  • 476