RFR: JDK-8257086: Clarify differences between {Float, Double}.equals and ==
Stuart Marks
smarks at openjdk.java.net
Fri Jan 29 03:19:39 UTC 2021
On Thu, 28 Jan 2021 07:15:05 GMT, Joe Darcy <darcy at openjdk.org> wrote:
>> Great, let me know if you'd like me to provide some text for any particular topics in this area.
>
>>
>>
>> Great, let me know if you'd like me to provide some text for any particular topics in this area.
>
> Before sending out another iteration in code, there the draft javadoc text of a discussion at the end of the class-level discussion of java.lang.Double:
>
> * <h2><a id=equivalenceRelation>Floating-point Equality, Equivalence,
> * and Comparison</a></h2>
> *
> * IEEE 754 floating-point values include finite nonzero values,
> * signed zeros ({@code +0.0} and {@code -0.0}), signed infinities
> * {@linkplain Double#POSITIVE_INFINITY positive infinity} and
> * {@linkplain Double#NEGATIVE_INFINITY negative infinity}), and
> * {@linkplain Double#NaN NaN} (not-a-number).
> *
> * <p>An <em>equivalence relation</em> on a set of values is a boolean
> * relation on pairs of values that is reflexive, symmetric, and
> * transitive. A set can have multiple equivalence relations defined
> * for it. For example, {@link java.util.HashMap HashMap} compares
> * keys in the set of objects using the equivalence relation of {@link
> * Object#equals Object.equals} while {@link java.util.IdentityHashMap
> * IdentityHashMap} compares keys in the set of objects using the
> * equivalence relation of reference equality.
> *
> * <p>For floating-point values to be used in data structures like
> * sets and maps, the {@linkplain Double#equals equals} or {@linkplain
> * Double#compareTo comparison} method must satisfy the usual
> * requirements of being an equivalence relation or analogous
> * requirement for comparison methods. However, surprisingly, the
> * built-in {@code ==} operation on floating-point values does
> * <em>not</em> implement an equivalence relation. Despite not
> * defining an equivalence relation, the semantics of the IEEE 754
> * {@code ==} operator were deliberately designed to meet other needs
> * of numerical computation. There are two exceptions where the
> * properties of an equivalence relations are not satisfied by {@code
> * ==} on floating-point values:
> *
> * <ul>
> *
> * <li>If {@code v1} and {@code v2} are both NaN, then {@code v1
> * == v2} has the value {@code false}. Therefore, for two NaN
> * arguments the <em>reflexive</em> property of an equivalence
> * relation is <em>not</em> satisfied by the {@code ==} operator.
> *
> * <li>If {@code v1} represents {@code +0.0} while {@code v2}
> * represents {@code -0.0}, or vice versa, then {@code v1 == v2} has
> * the value {@code true} even though {@code +0.0} and {@code -0.0}
> * are distinguishable under various floating-point operations. For
> * example, {@code 1.0/+0.0} evaluates to positive infinity while
> * {@code 1.0/-0.0} evaluates to <em>negative</em> infinity and
> * positive infinity and negative infinity are neither equal to each
> * other nor equivalent to each other.
> *
> * </ul>
> *
> * <p>For ordered comparisons using the built-in comparison operator
> * ({@code <}, {@code <=}, etc.), NaN values have another anomalous
> * situation: a NaN is neither less than, greater than, nor equal to
> * any value, including itself. This means the <em>trichotomy of
> * comparison</em> does <em>not</em> hold.
> *
> * <p>To provide the appropriate semantics for {@code equals} and {@code
> * compareTo} methods, those methods cannot simply to wrappers around
> * {@code ==} or ordered comparison operations. Instead, {@link
> * Double#equals equals} defines NaN arguments to be equal to each
> * other and defines {@code +0.0} to <em>not</em> be equal to {@code
> * -0.0}, restoring reflexivity. For comparisons, {@link
> * Double#compareTo compareTo} defines a total order where {@code
> * -0.0} is less than {@code +0.0} and where a NaN is equal to itself
> * and considered greater than positive infinity.
> *
> * <p>The operational semantics of {@code equals} and {@code
> * compareTo} are expressed in terms of {@linkplain doubleToLongBigs
> * bit-wise converting} the floating-point values to integral values
> *
> * <p>The <em>natural ordering</em> implemented by {@link compareTo
> * compareTo} is {@linkplain Comparable consistent with equals}; that
> * is values are only reported as equal by {@code equals} if {@code
> * compareTo} on those objects returns zero.
> *
> * @jls 4.2.3 Floating-Point Types, Formats, and Values
> * @jls 4.2.4. Floating-Point Operations
> * @jls 15.21.1 Numerical Equality Operators == and !=
>
> Comments?
>
> Thanks,
I took the liberty of making an editing pass over the proposed text. Along with a few editorial and markup adjustments, the key changes are as follows:
1) I moved discussion of interaction with Set and Map to the end, after all the issues have been set up.
2) I added the concept of substitution alongside the equivalence relation. AFAIK substitution (or substitutability) is not part of an equivalence relation, but it seems that in many systems, equivalence implies substitutability. Of course, the point is that it _doesn't_ apply for -0.0 and +0.0.
We'll see how well this survives github markup. If it doesn't work out, I can email the text file. (Triple backquotes seem to do the trick.)
* <h2><a id=equivalenceRelation>Floating-point Equality, Equivalence,
* and Comparison</a></h2>
*
* IEEE 754 floating-point values include finite nonzero values,
* signed zeros ({@code +0.0} and {@code -0.0}), signed infinities
* {@linkplain Double#POSITIVE_INFINITY positive infinity} and
* {@linkplain Double#NEGATIVE_INFINITY negative infinity}), and
* {@linkplain Double#NaN NaN} (not-a-number).
*
* <p>An <em>equivalence relation</em> on a set of values is a boolean
* relation on pairs of values that is reflexive, symmetric, and
* transitive. For more discussion of equivalence relations, see
* the {@link Object#equals Object.equals} specification. Additionally,
* In a numeric expression, values that are equivalent can generally
* be <em>substituted</em> for one another without changing the value
* of the expression, though there are exceptions.
*
* <p>Notably, the
* built-in {@code ==} operation on floating-point values does
* <em>not</em> implement an equivalence relation. Despite not
* defining an equivalence relation, the semantics of the IEEE 754
* {@code ==} operator were deliberately designed to meet other needs
* of numerical computation. There are two exceptions where the
* properties of an equivalence relations are not satisfied by {@code
* ==} on floating-point values:
*
* <ul>
*
* <li>If {@code v1} and {@code v2} are both NaN, then {@code v1
* == v2} has the value {@code false}. Therefore, for two NaN
* arguments the <em>reflexive</em> property of an equivalence
* relation is <em>not</em> satisfied by the {@code ==} operator.
*
* <li>If {@code v1} represents {@code +0.0} while {@code v2}
* represents {@code -0.0}, or vice versa, then {@code v1 == v2} has
* the value {@code true} even though {@code +0.0} and {@code -0.0}
* are distinguishable under various floating-point operations. For
* example, {@code 1.0/+0.0} evaluates to positive infinity while
* {@code 1.0/-0.0} evaluates to <em>negative</em> infinity and
* positive infinity and negative infinity are neither equal to each
* other nor equivalent to each other. Thus, {@code +0.0} and {@code
* -0.0} may not be substituted for each other in general.
*
* </ul>
*
* <p>For ordered comparisons using the built-in comparison operators
* ({@code <}, {@code <=}, etc.), NaN values have another anomalous
* situation: a NaN is neither less than, greater than, nor equal to
* any value, including itself. This means the <em>trichotomy of
* comparison</em> does <em>not</em> hold.
*
* <p>To provide the appropriate semantics for {@code equals} and {@code
* compareTo} methods, those methods cannot simply to wrappers around
* {@code ==} or ordered comparison operations. Instead, {@link
* Double#equals equals} defines NaN arguments to be equal to each
* other and defines {@code +0.0} to <em>not</em> be equal to {@code
* -0.0}, restoring reflexivity. For comparisons, {@link
* Double#compareTo compareTo} defines a total order where {@code
* -0.0} is less than {@code +0.0} and where a NaN is equal to itself
* and considered greater than positive infinity.
*
* <p>The operational semantics of {@code equals} and {@code
* compareTo} are expressed in terms of {@linkplain #doubleToLongBits
* bit-wise converting} the floating-point values to integral values.
*
* <p>The <em>natural ordering</em> implemented by {@link #compareTo
* compareTo} is {@linkplain Comparable consistent with equals}. That
* is, its values are reported as equal by {@code equals} if and only
* if {@code compareTo} on those objects returns zero.
*
* <p>The adjusted behaviors defined for {@code equals} and {@code
* compareTo} allow instances of wrapper classes to work properly with
* conventional data structures. For example, defining NaN
* values to be {@code equals} to one another allows NaN to be used as
* an element of a {@link java.util.HashSet HashSet} or as the key of
* a {@link java.util.HashMap HashMap}. Similarly, defining {@code
* compareTo} as a total ordering, including {@code +0.0}, {@code
* -0.0}, and NaN, allows instances of wrapper classes to be used as
* elements of a {@link java.util.SortedSet SortedSet} or as keys of a
* {@link java.util.SortedMap SortedMap}.
*
* @jls 4.2.3 Floating-Point Types, Formats, and Values
* @jls 4.2.4. Floating-Point Operations
* @jls 15.21.1 Numerical Equality Operators == and !=
-------------
PR: https://git.openjdk.java.net/jdk/pull/1699
More information about the core-libs-dev
mailing list