diff --git a/system/doc/reference_manual/data_types.xml b/system/doc/reference_manual/data_types.xml index 4b58c6a5ca81..a7916b441270 100644 --- a/system/doc/reference_manual/data_types.xml +++ b/system/doc/reference_manual/data_types.xml @@ -87,6 +87,37 @@ 11> 1_234.333_333 1234.333333 + +
+ Comparisons +

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-equivalence Term 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.

+