Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8202555

Double.toString(double) sometimes produces incorrect results

    XMLWordPrintable

    Details

    • Type: CSR
    • Status: Closed
    • Priority: P3
    • Resolution: Approved
    • Fix Version/s: 19
    • Component/s: core-libs
    • Labels:
      None
    • Subcomponent:
    • Compatibility Kind:
      behavioral
    • Compatibility Risk:
      minimal
    • Compatibility Risk Description:
      Hide
      The current specification does not always uniquely specify the resulting string. In addition, there are many documented anomalies in the implementation (JDK-4511638) that are still present. Thus, the risk of encountering slightly different results is latent since ever and can potentially manifest at each release of the platform: any correction, even without a spec modification, would introduce slight incompatibilities anyway.

      The proposed specification aims at uniquely determine the resulting value. When accompanied by a conforming implementation, it should help removing the uncertainties associated with the current, more vague spec.

      The behavior changes will also be visible in certain other parts of the platform, including java.math (JDK-8205592).
      Show
      The current specification does not always uniquely specify the resulting string. In addition, there are many documented anomalies in the implementation ( JDK-4511638 ) that are still present. Thus, the risk of encountering slightly different results is latent since ever and can potentially manifest at each release of the platform: any correction, even without a spec modification, would introduce slight incompatibilities anyway. The proposed specification aims at uniquely determine the resulting value. When accompanied by a conforming implementation, it should help removing the uncertainties associated with the current, more vague spec. The behavior changes will also be visible in certain other parts of the platform, including java.math ( JDK-8205592 ).
    • Interface Kind:
      Java API
    • Scope:
      SE

      Description

      Summary

      Modify the specification (Javadoc) of Double::toString(double) and Float::toString(float) to ensure a uniquely determined resulting string value in all cases.

      Problem

      The current Javadoc specifications of the mentioned methods are somehow vague in what the resulting strings should be.

      On the one hand, a strict reading leads to believe that the digits in the resulting string are drawn, from left to right, from the exact value of the argument, until the number represented by the string is near enough to the argument as to round to it according to the default IEEE 754 round-to-closest mode.

      On the other hand, a more lenient interpretation of the Javadoc and the observed behavior both lead to the conclusion that the digits appearing in the result are those of an unspecified number that also rounds to the argument. While the spec makes it clear that it must be a shortest one that possibly rounds to the argument, sometimes there are more choices. The spec says nothing in these cases. In summary, it is not always clear from which number the digits are eventually drawn. In absence of a more specific description of this number, the result is not always uniquely determined and different implementations are thus allowed to return different strings.

      Solution

      Specify that the conversion is split in two separate stages. The first selects a unique, well specified decimal number that represents the argument and that meets the properties listed in the specification section below. The second stage then format this decimal number as a string, as specified below.

      The current and the proposed specs, while different in wording, determine exactly the same resulting strings for the vast majority of cases. Their results, however, might differ where the current one is not specific enough.

      Specification

      Double::toString(double):

          /**
           * Returns a string representation of the {@code double}
           * argument. All characters mentioned below are ASCII characters.
           * <ul>
           * <li>If the argument is NaN, the result is the string
           *     "{@code NaN}".
           * <li>Otherwise, the result is a string that represents the sign and
           * magnitude (absolute value) of the argument. If the sign is negative,
           * the first character of the result is '{@code -}'
           * ({@code '\u005Cu002D'}); if the sign is positive, no sign character
           * appears in the result. As for the magnitude <i>m</i>:
           * <ul>
           * <li>If <i>m</i> is infinity, it is represented by the characters
           * {@code "Infinity"}; thus, positive infinity produces the result
           * {@code "Infinity"} and negative infinity produces the result
           * {@code "-Infinity"}.
           *
           * <li>If <i>m</i> is zero, it is represented by the characters
           * {@code "0.0"}; thus, negative zero produces the result
           * {@code "-0.0"} and positive zero produces the result
           * {@code "0.0"}.
           *
           * <li> Otherwise <i>m</i> is positive and finite.
           * It is converted to a string in two stages:
           * <ul>
           * <li> <em>Selection of a decimal</em>:
           * A well-defined decimal <i>d</i><sub><i>m</i></sub>
           * is selected to represent <i>m</i>.
           * This decimal is (almost always) the <em>shortest</em> one that
           * rounds to <i>m</i> according to the round to nearest
           * rounding policy of IEEE 754 floating-point arithmetic.
           * <li> <em>Formatting as a string</em>:
           * The decimal <i>d</i><sub><i>m</i></sub> is formatted as a string,
           * either in plain or in computerized scientific notation,
           * depending on its value.
           * </ul>
           * </ul>
           * </ul>
           *
           * <p>A <em>decimal</em> is a number of the form
           * <i>s</i>&times;10<sup><i>i</i></sup>
           * for some (unique) integers <i>s</i> &gt; 0 and <i>i</i> such that
           * <i>s</i> is not a multiple of 10.
           * These integers are the <em>significand</em> and
           * the <em>exponent</em>, respectively, of the decimal.
           * The <em>length</em> of the decimal is the (unique)
           * positive integer <i>n</i> meeting
           * 10<sup><i>n</i>-1</sup> &le; <i>s</i> &lt; 10<sup><i>n</i></sup>.
           *
           * <p>The decimal <i>d</i><sub><i>m</i></sub> for a finite positive <i>m</i>
           * is defined as follows:
           * <ul>
           * <li>Let <i>R</i> be the set of all decimals that round to <i>m</i>
           * according to the usual <em>round to nearest</em> rounding policy of
           * IEEE 754 floating-point arithmetic.
           * <li>Let <i>p</i> be the minimal length over all decimals in <i>R</i>.
           * <li>When <i>p</i> &ge; 2, let <i>T</i> be the set of all decimals
           * in <i>R</i> with length <i>p</i>.
           * Otherwise, let <i>T</i> be the set of all decimals
           * in <i>R</i> with length 1 or 2.
           * <li>Define <i>d</i><sub><i>m</i></sub> as the decimal in <i>T</i>
           * that is closest to <i>m</i>.
           * Or if there are two such decimals in <i>T</i>,
           * select the one with the even significand.
           * </ul>
           *
           * <p>The (uniquely) selected decimal <i>d</i><sub><i>m</i></sub>
           * is then formatted.
           * Let <i>s</i>, <i>i</i> and <i>n</i> be the significand, exponent and
           * length of <i>d</i><sub><i>m</i></sub>, respectively.
           * Further, let <i>e</i> = <i>n</i> + <i>i</i> - 1 and let
           * <i>s</i><sub>1</sub>&hellip;<i>s</i><sub><i>n</i></sub>
           * be the usual decimal expansion of <i>s</i>.
           * Note that <i>s</i><sub>1</sub> &ne; 0
           * and <i>s</i><sub><i>n</i></sub> &ne; 0.
           * Below, the decimal point <code>.</code> is {@code '\u005Cu002E'}
           * and the exponent indicator <code>E</code> is {@code '\u005Cu0045'}.
           * <ul>
           * <li>Case -3 &le; <i>e</i> &lt; 0:
           * <i>d</i><sub><i>m</i></sub> is formatted as
           * <code>0.0</code>&hellip;<code>0</code><!--
           * --><i>s</i><sub>1</sub>&hellip;<i>s</i><sub><i>n</i></sub>,
           * where there are exactly -(<i>n</i> + <i>i</i>) zeroes between
           * the decimal point and <i>s</i><sub>1</sub>.
           * For example, 123 &times; 10<sup>-4</sup> is formatted as
           * {@code 0.0123}.
           * <li>Case 0 &le; <i>e</i> &lt; 7:
           * <ul>
           * <li>Subcase <i>i</i> &ge; 0:
           * <i>d</i><sub><i>m</i></sub> is formatted as
           * <i>s</i><sub>1</sub>&hellip;<i>s</i><sub><i>n</i></sub><!--
           * --><code>0</code>&hellip;<code>0.0</code>,
           * where there are exactly <i>i</i> zeroes
           * between <i>s</i><sub><i>n</i></sub> and the decimal point.
           * For example, 123 &times; 10<sup>2</sup> is formatted as
           * {@code 12300.0}.
           * <li>Subcase <i>i</i> &lt; 0:
           * <i>d</i><sub><i>m</i></sub> is formatted as
           * <i>s</i><sub>1</sub>&hellip;<!--
           * --><i>s</i><sub><i>n</i>+<i>i</i></sub><code>.</code><!--
           * --><i>s</i><sub><i>n</i>+<i>i</i>+1</sub>&hellip;<!--
           * --><i>s</i><sub><i>n</i></sub>,
           * where there are exactly -<i>i</i> digits to the right of
           * the decimal point.
           * For example, 123 &times; 10<sup>-1</sup> is formatted as
           * {@code 12.3}.
           * </ul>
           * <li>Case <i>e</i> &lt; -3 or <i>e</i> &ge; 7:
           * computerized scientific notation is used to format
           * <i>d</i><sub><i>m</i></sub>.
           * Here <i>e</i> is formatted as by {@link Integer#toString(int)}.
           * <ul>
           * <li>Subcase <i>n</i> = 1:
           * <i>d</i><sub><i>m</i></sub> is formatted as
           * <i>s</i><sub>1</sub><code>.0E</code><i>e</i>.
           * For example, 1 &times; 10<sup>23</sup> is formatted as
           * {@code 1.0E23}.
           * <li>Subcase <i>n</i> &gt; 1:
           * <i>d</i><sub><i>m</i></sub> is formatted as
           * <i>s</i><sub>1</sub><code>.</code><i>s</i><sub>2</sub><!--
           * -->&hellip;<i>s</i><sub><i>n</i></sub><code>E</code><i>e</i>.
           * For example, 123 &times; 10<sup>-21</sup> is formatted as
           * {@code 1.23E-19}.
           * </ul>
           * </ul>
           *
           * <p>To create localized string representations of a floating-point
           * value, use subclasses of {@link java.text.NumberFormat}.
           *
           * @param   d   the {@code double} to be converted.
           * @return a string representation of the argument.
           */
          public static String toString(double d) {}

      Float::toString(float):

          /**
           * Returns a string representation of the {@code float}
           * argument. All characters mentioned below are ASCII characters.
           * <ul>
           * <li>If the argument is NaN, the result is the string
           * "{@code NaN}".
           * <li>Otherwise, the result is a string that represents the sign and
           *     magnitude (absolute value) of the argument. If the sign is
           *     negative, the first character of the result is
           *     '{@code -}' ({@code '\u005Cu002D'}); if the sign is
           *     positive, no sign character appears in the result. As for
           *     the magnitude <i>m</i>:
           * <ul>
           * <li>If <i>m</i> is infinity, it is represented by the characters
           *     {@code "Infinity"}; thus, positive infinity produces
           *     the result {@code "Infinity"} and negative infinity
           *     produces the result {@code "-Infinity"}.
           * <li>If <i>m</i> is zero, it is represented by the characters
           *     {@code "0.0"}; thus, negative zero produces the result
           *     {@code "-0.0"} and positive zero produces the result
           *     {@code "0.0"}.
           *
           * <li> Otherwise <i>m</i> is positive and finite.
           * It is converted to a string in two stages:
           * <ul>
           * <li> <em>Selection of a decimal</em>:
           * A well-defined decimal <i>d</i><sub><i>m</i></sub>
           * is selected to represent <i>m</i>.
           * This decimal is (almost always) the <em>shortest</em> one that
           * rounds to <i>m</i> according to the round to nearest
           * rounding policy of IEEE 754 floating-point arithmetic.
           * <li> <em>Formatting as a string</em>:
           * The decimal <i>d</i><sub><i>m</i></sub> is formatted as a string,
           * either in plain or in computerized scientific notation,
           * depending on its value.
           * </ul>
           * </ul>
           * </ul>
           *
           * <p>A <em>decimal</em> is a number of the form
           * <i>s</i>&times;10<sup><i>i</i></sup>
           * for some (unique) integers <i>s</i> &gt; 0 and <i>i</i> such that
           * <i>s</i> is not a multiple of 10.
           * These integers are the <em>significand</em> and
           * the <em>exponent</em>, respectively, of the decimal.
           * The <em>length</em> of the decimal is the (unique)
           * positive integer <i>n</i> meeting
           * 10<sup><i>n</i>-1</sup> &le; <i>s</i> &lt; 10<sup><i>n</i></sup>.
           *
           * <p>The decimal <i>d</i><sub><i>m</i></sub> for a finite positive <i>m</i>
           * is defined as follows:
           * <ul>
           * <li>Let <i>R</i> be the set of all decimals that round to <i>m</i>
           * according to the usual <em>round to nearest</em> rounding policy of
           * IEEE 754 floating-point arithmetic.
           * <li>Let <i>p</i> be the minimal length over all decimals in <i>R</i>.
           * <li>When <i>p</i> &ge; 2, let <i>T</i> be the set of all decimals
           * in <i>R</i> with length <i>p</i>.
           * Otherwise, let <i>T</i> be the set of all decimals
           * in <i>R</i> with length 1 or 2.
           * <li>Define <i>d</i><sub><i>m</i></sub> as the decimal in <i>T</i>
           * that is closest to <i>m</i>.
           * Or if there are two such decimals in <i>T</i>,
           * select the one with the even significand.
           * </ul>
           *
           * <p>The (uniquely) selected decimal <i>d</i><sub><i>m</i></sub>
           * is then formatted.
           * Let <i>s</i>, <i>i</i> and <i>n</i> be the significand, exponent and
           * length of <i>d</i><sub><i>m</i></sub>, respectively.
           * Further, let <i>e</i> = <i>n</i> + <i>i</i> - 1 and let
           * <i>s</i><sub>1</sub>&hellip;<i>s</i><sub><i>n</i></sub>
           * be the usual decimal expansion of <i>s</i>.
           * Note that <i>s</i><sub>1</sub> &ne; 0
           * and <i>s</i><sub><i>n</i></sub> &ne; 0.
           * Below, the decimal point <code>.</code> is {@code '\u005Cu002E'}
           * and the exponent indicator <code>E</code> is {@code '\u005Cu0045'}.
           * <ul>
           * <li>Case -3 &le; <i>e</i> &lt; 0:
           * <i>d</i><sub><i>m</i></sub> is formatted as
           * <code>0.0</code>&hellip;<code>0</code><!--
           * --><i>s</i><sub>1</sub>&hellip;<i>s</i><sub><i>n</i></sub>,
           * where there are exactly -(<i>n</i> + <i>i</i>) zeroes between
           * the decimal point and <i>s</i><sub>1</sub>.
           * For example, 123 &times; 10<sup>-4</sup> is formatted as
           * {@code 0.0123}.
           * <li>Case 0 &le; <i>e</i> &lt; 7:
           * <ul>
           * <li>Subcase <i>i</i> &ge; 0:
           * <i>d</i><sub><i>m</i></sub> is formatted as
           * <i>s</i><sub>1</sub>&hellip;<i>s</i><sub><i>n</i></sub><!--
           * --><code>0</code>&hellip;<code>0.0</code>,
           * where there are exactly <i>i</i> zeroes
           * between <i>s</i><sub><i>n</i></sub> and the decimal point.
           * For example, 123 &times; 10<sup>2</sup> is formatted as
           * {@code 12300.0}.
           * <li>Subcase <i>i</i> &lt; 0:
           * <i>d</i><sub><i>m</i></sub> is formatted as
           * <i>s</i><sub>1</sub>&hellip;<!--
           * --><i>s</i><sub><i>n</i>+<i>i</i></sub><code>.</code><!--
           * --><i>s</i><sub><i>n</i>+<i>i</i>+1</sub>&hellip;<!--
           * --><i>s</i><sub><i>n</i></sub>,
           * where there are exactly -<i>i</i> digits to the right of
           * the decimal point.
           * For example, 123 &times; 10<sup>-1</sup> is formatted as
           * {@code 12.3}.
           * </ul>
           * <li>Case <i>e</i> &lt; -3 or <i>e</i> &ge; 7:
           * computerized scientific notation is used to format
           * <i>d</i><sub><i>m</i></sub>.
           * Here <i>e</i> is formatted as by {@link Integer#toString(int)}.
           * <ul>
           * <li>Subcase <i>n</i> = 1:
           * <i>d</i><sub><i>m</i></sub> is formatted as
           * <i>s</i><sub>1</sub><code>.0E</code><i>e</i>.
           * For example, 1 &times; 10<sup>23</sup> is formatted as
           * {@code 1.0E23}.
           * <li>Subcase <i>n</i> &gt; 1:
           * <i>d</i><sub><i>m</i></sub> is formatted as
           * <i>s</i><sub>1</sub><code>.</code><i>s</i><sub>2</sub><!--
           * -->&hellip;<i>s</i><sub><i>n</i></sub><code>E</code><i>e</i>.
           * For example, 123 &times; 10<sup>-21</sup> is formatted as
           * {@code 1.23E-19}.
           * </ul>
           * </ul>
           *
           * <p>To create localized string representations of a floating-point
           * value, use subclasses of {@link java.text.NumberFormat}.
           *
           * @param   f   the float to be converted.
           * @return a string representation of the argument.
           */
          public static String toString(float f) {}

        Attachments

        1. Double.diff
          8 kB
        2. Float.diff
          8 kB
        3. specdiff-4511638.00.zip
          171 kB
        4. specdiff-4511638.01.zip
          193 kB

          Issue Links

            Activity

              People

              Assignee:
              rgiulietti Raffaello Giulietti
              Reporter:
              bstrathesunw Bill Strathearn (Inactive)
              Reviewed By:
              Brian Burkhalter, Guy Steele
              Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: