erlang / otp

Erlang/OTP
http://erlang.org
Apache License 2.0
11.38k stars 2.95k forks source link

ERL-642: 0.0 is damaged by erlang:list_to_float/1 and erlang:binary_to_float/1 #3628

Closed OTP-Maintainer closed 3 years ago

OTP-Maintainer commented 6 years ago

Original reporter: sebastian Affected version: Not Specified Component: kernel Migrated from: https://bugs.erlang.org/browse/ERL-642


The calls {{erlang:list_to_float("-0.0")}} and {{erlang:binary_to_float(<<"-0.0">>)}} have the unexpected side-effect of changing the constant {{0.0}} into {{-0.0}}, i.e. the float represented by {{<<1:1,0:63>>}}, within a local function.

I am attaching the program {{a.erl} exposing this beheviour.

Workaround: Wrap calls to these NIFs into a function of the Erlang VM. This is also illustrated in the attached program
OTP-Maintainer commented 6 years ago

sverker said:

This problem stems from the questionable design "choice" to make {{0.0 =:= -0.0}}.

I don't think it's directly caused by list/binary_to_float by rather the compiler doing some sort of constant folding optimization which changes 0.0 to -0.0 as they are "the same".
OTP-Maintainer commented 6 years ago

sebastian said:

The byte code shows that the NIFs were evaluated properly at compile-time, corroborating your suggestion that the bug is in the compiler.

Unfortunately, the byte code loads two +0.0 for calling {{f/2}}, but {{f/2}} chokes on a bad +0.0, not on the -0.0. So there must be some monkey business going on with the constants themselves.
OTP-Maintainer commented 6 years ago

lukas said:

I think that the only sane thing that we can do is to forbid -0.0 completely as we do with NAN and INF. This would effect binary_to_float, binary_to_term and <<F/float>>. However this breaks backwards compatibility, which may not be ok.

Other options are:
* Auto convert -0.0 to 0.0 at ingress points.
** This approach works pretty well but creates some strange scenarios, similar to what you have seen:
{noformat}
<<F1/float>> = B1 = <<1:1,0:63>>, %% F1 is auto converted from -0.0 to 0.0.
B2 = <<F1/float>>, %% B2 now gets the +0.0 representation
false = B1 == B2.
{noformat}
* Make the compiler not use normal equivalence, but instead use a special equivalence where -0.0 =/= 0.0.
** Using a special equal is error prone and slow.
* Don't allow the compiler to evaluate in compile time binary_to_float, list_to_float or <<F/float>> matches.
** Solves the problem for this specific problem, but there may be more and this doesn't make the language as such more consistent.

Anyone that has other opinions feel free to chime in.
OTP-Maintainer commented 5 years ago

sverker said:

We don't see any way to "fix" this, without breaking some sort of backward compatibility, as in Erlang as well as many other languages +0.0 and -0.0 compare (match) as equal.