rescript-lang / rescript-compiler

The compiler for ReScript.

Home Page:https://rescript-lang.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ReScript force normalizing `-0` to `0`

cometkim opened this issue · comments

let a = -0;

produces

var a = 0;

-0 is is an IEEE 754 standard, and JavaScript strictly follows it. This can cause problems in code that relies on this strict distinction, such as Object.is

Adding here some ChatGPT explanation

Prompt: "Explain why JavaScript distinguishes 0 and -0. Also please let me know if any potential problems may arise when normalizing all -0 to 0"

ChatGPT answer

JavaScript's distinction between 0 and -0 originates from its use of the IEEE 754 standard for floating point arithmetic. In this standard, each number is represented by a sign bit, an exponent, and a significand. Both 0 and -0 are represented with all bits of the exponent and significand as zero, but they differ in their sign bit. 0 has a sign bit of 0 and -0 has a sign bit of 1.

This distinction is useful in certain mathematical computations, particularly those involving limits, derivatives, or integrals in calculus, where the direction of approach towards zero can affect the result. For example, in functions that compute the slope of a tangent or the rate of change, knowing the direction from which zero is approached (positive or negative) can be crucial.

Potential Problems Arising from Normalizing -0 to 0:

  1. Loss of Mathematical Precision: Certain mathematical operations and algorithms rely on the distinction between 0 and -0 to function correctly. For example, the function 1/x approaches infinity as x approaches 0 from the positive side, and negative infinity as x approaches from the negative side. Normalizing -0 to 0 can lead to incorrect results in these cases.

  2. Impact on Sign-sensitive Operations: Functions and operations that are sensitive to the sign of a number, such as Math.sign(), will behave differently if -0 is treated as 0. In JavaScript, Math.sign(-0) returns -1, indicating the negative sign, whereas Math.sign(0) returns 0. Removing the distinction would affect the outcomes of such functions.

  3. Issues in Floating Point Comparisons: In JavaScript, 0 === -0 returns true, but there are some low-level operations or optimized algorithms that might rely on the binary distinction for checks or optimizations. Uniformly treating both as 0 could disrupt these processes.

  4. Implications for Array and Object Keys: In JavaScript, if -0 and 0 are used as keys in objects or arrays, they are treated identically. However, changing how -0 is treated might affect legacy systems or algorithms that have been designed with the existing distinction in mind.

In conclusion, while it might seem redundant or unnecessary to distinguish between 0 and -0, doing so aligns with the precision and requirements of numerical computations in programming and adheres to widely accepted standards in computational mathematics. Removing this distinction can lead to subtle bugs and inaccuracies in complex systems.

let a = -0;

This is an int literal. IEEE 754 is the specification for floats. If you make it a float literal it will behave as you expect:

let a = -0.;
var a = -0;

I see, yet again #6737 😅

It's subtle that this problem continues to occur when simply casting Int<->Float for interacting with JS bindings

See my answer in #6737 (comment). It's really no more surprising than 1.1 being translated to 1. These are different types that represent different sets of values. float to int conversion is by necessity lossy, and there needs to be some kind of mapping between them. You can of course argue that the mapping should be different, but -0 is not a value that can be represented as an int. If you need to make that distinction you should probably use float instead.

I don't think there's a problem with the overall ReScript semantics. However, it inherits some of in the JS ecosystem where all numbers can potentially be converted to floating.

When we write a library binding, we simply make an interface float to represent the JS numbers. I could say that is naive, but I think it's pretty conventional. (we officially use that example in the blog post)

Actually, even I'm pretty familiar with ReScript, at one point I made a bug (I simply made the initial value to infinity, because that's ok in JS) and didn't suspect it was the problem for a while.

I think we can at least generate a compiler warning like we do with the untagged variants.

It seems to be the same topic with #6737, I'm closing this to merge discussions. I will move my last comment.