Both integers and floats share the same linear order. That is,
+ 1 compares less than 2.4, 3 compares greater than
+ 2.99999, and 5 is equal to 5.0.
+
When wanting to compare an integer with another integer or a float
+ with another float, it may be tempting to use the term equivalence
+ operators (=:=, =/=) or pattern matching. This works for
+ integers which has a distinct representation for every number, but
+ there's a surprising edge case for floating-point as the latter has two
+ representations for zero which are considered different by the term
+ equivalence operator (=:=) and pattern matching.
+
If you wish to compare floating-point numbers numerically,
+ use the regular comparison operators (such as ==) and add guards
+ that require both the arguments to be floating-point.
+
+
Prior to OTP 27, the term equivalence operators had a bug where they
+ considered 0.0 and -0.0 to be the same term. Legacy
+ code that makes equality comparisons on floating-point zero should
+ migrate to using the equal-to (==) operator with
+ is_float/1 guards, and compiler warnings have been added to
+ that effect. These can be silenced by writing +0.0 instead,
+ which is the same as 0.0 but makes the compiler interpret the
+ comparison as being purposely made against 0.0.
+
Note that this does not break compatibility with IEEE 754
+ which mandates that 0.0 and -0.0 should compare
+ equal: they are equal when interpreted as numbers (==), and
+ unequal when interpreted as opaque terms (=:=).
+
+ Representation of Floating Point Numbers
diff --git a/system/doc/reference_manual/expressions.xml b/system/doc/reference_manual/expressions.xml
index 6b3ba96b6b13..77f46d5363ca 100644
--- a/system/doc/reference_manual/expressions.xml
+++ b/system/doc/reference_manual/expressions.xml
@@ -801,11 +801,11 @@ Expr1 op Expr2
=:=
- Exactly equal to
+ Term equivalence=/=
- Exactly not equal to
+ Term non-equivalenceTerm Comparison Operators.
@@ -836,6 +836,28 @@ number < atom < reference < fun < port < pid < tuple < map
depending on the size of the float because otherwise comparison of large
floats and integers would lose their transitivity.
+
The term equivalence operators, =:= and =/=, return
+ whether two terms are indistinguishable. While the other operators
+ consider the same numbers equal even when their types differ
+ (1 == 1.0 is true), the term equivalence operators return whether
+ there exists any function that can tell their arguments apart.
+
+
For example, while the terms 0 and 0.0 represent the same
+ number, we can tell them apart by using the is_integer/1
+ function. Hence, =:= and =/= consider them different.
+
+
Furthermore, the terms 0.0 and -0.0 also represent the
+ same number, but they yield different results when converted to
+ string form through float_to_list/1: when given the former it
+ returns a string without a sign, and when given the latter it returns a
+ string with a sign. Therefore, =:= and =/= consider
+ them different.
+
+
The term equivalence operators are useful when reasoning about terms as
+ opaque values, for example in associative containers or memoized
+ functions where using the equal-to operator (==) can result in
+ subtly incorrect results.
+
Term comparison operators return the Boolean value of the
expression, true or false.
@@ -845,17 +867,35 @@ number < atom < reference < fun < port < pid < tuple < map
true
2> 1=:=1.0.
false
-3> 1 > a.
+3> 0=:=0.0.
false
-4> #{c => 3} > #{a => 1, b => 2}.
+4> 0.0=:=-0.0.
false
-5> #{a => 1, b => 2} == #{a => 1.0, b => 2.0}.
+5> 0.0=:=+0.0.
true
-6> <<2:2>> < <<128>>.
+6> 1 > a.
+false
+7> #{c => 3} > #{a => 1, b => 2}.
+false
+8> #{a => 1, b => 2} == #{a => 1.0, b => 2.0}.
true
-7> <<3:2>> < <<128>>.
+9> <<2:2>> < <<128>>.
+true
+10> <<3:2>> < <<128>>.
false
+
+
Prior to OTP 27, the term equivalence operators had a bug where they
+ considered 0.0 and -0.0 to be the same term.
+
This was fixed in OTP 27 but legacy code may have expected them to
+ be considered the same. To help users catch errors that may arise from
+ an upgrade, the compiler raises a warning when 0.0 is
+ pattern-matched or used in a term equivalence test.
+
If you need to match 0.0 specifically, then the warning can be
+ silenced by writing +0.0 instead, which produces the same term
+ but makes the compiler interpret the match as being done on
+ purpose.