Skip to content

Commit

Permalink
4511638: Double.toString(double) sometimes produces incorrect results
Browse files Browse the repository at this point in the history
Reviewed-by: aturbanov, darcy, bpb
  • Loading branch information
rgiulietti authored and jddarcy committed Jun 1, 2022
1 parent 2f19144 commit 72bcf2a
Show file tree
Hide file tree
Showing 18 changed files with 4,076 additions and 121 deletions.
17 changes: 14 additions & 3 deletions src/java.base/share/classes/java/lang/AbstractStringBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@

package java.lang;

import jdk.internal.math.FloatingDecimal;
import jdk.internal.math.DoubleToDecimal;
import jdk.internal.math.FloatToDecimal;

import java.io.IOException;
import java.util.Arrays;
import java.util.Spliterator;
import java.util.stream.IntStream;
Expand Down Expand Up @@ -875,8 +877,13 @@ public AbstractStringBuilder append(long l) {
* @return a reference to this object.
*/
public AbstractStringBuilder append(float f) {
FloatingDecimal.appendTo(f,this);
try {
FloatToDecimal.appendTo(f, this);
} catch (IOException e) {
throw new AssertionError(e);
}
return this;

}

/**
Expand All @@ -892,7 +899,11 @@ public AbstractStringBuilder append(float f) {
* @return a reference to this object.
*/
public AbstractStringBuilder append(double d) {
FloatingDecimal.appendTo(d,this);
try {
DoubleToDecimal.appendTo(d, this);
} catch (IOException e) {
throw new AssertionError(e);
}
return this;
}

Expand Down
135 changes: 103 additions & 32 deletions src/java.base/share/classes/java/lang/Double.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

import jdk.internal.math.FloatingDecimal;
import jdk.internal.math.DoubleConsts;
import jdk.internal.math.DoubleToDecimal;
import jdk.internal.vm.annotation.IntrinsicCandidate;

/**
Expand Down Expand Up @@ -280,39 +281,109 @@ public final class Double extends Number
* {@code "-0.0"} and positive zero produces the result
* {@code "0.0"}.
*
* <li>If <i>m</i> is greater than or equal to 10<sup>-3</sup> but less
* than 10<sup>7</sup>, then it is represented as the integer part of
* <i>m</i>, in decimal form with no leading zeroes, followed by
* '{@code .}' ({@code '\u005Cu002E'}), followed by one or
* more decimal digits representing the fractional part of <i>m</i>.
*
* <li>If <i>m</i> is less than 10<sup>-3</sup> or greater than or
* equal to 10<sup>7</sup>, then it is represented in so-called
* "computerized scientific notation." Let <i>n</i> be the unique
* integer such that 10<sup><i>n</i></sup> &le; <i>m</i> {@literal <}
* 10<sup><i>n</i>+1</sup>; then let <i>a</i> be the
* mathematically exact quotient of <i>m</i> and
* 10<sup><i>n</i></sup> so that 1 &le; <i>a</i> {@literal <} 10. The
* magnitude is then represented as the integer part of <i>a</i>,
* as a single decimal digit, followed by '{@code .}'
* ({@code '\u005Cu002E'}), followed by decimal digits
* representing the fractional part of <i>a</i>, followed by the
* letter '{@code E}' ({@code '\u005Cu0045'}), followed
* by a representation of <i>n</i> as a decimal integer, as
* produced by the method {@link Integer#toString(int)}.
* <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 '.'} is {@code '\u005Cu002E'}
* and the exponent indicator {@code 'E'} 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>
* How many digits must be printed for the fractional part of
* <i>m</i> or <i>a</i>? There must be at least one digit to represent
* the fractional part, and beyond that as many, but only as many, more
* digits as are needed to uniquely distinguish the argument value from
* adjacent values of type {@code double}. That is, suppose that
* <i>x</i> is the exact mathematical value represented by the decimal
* representation produced by this method for a finite nonzero argument
* <i>d</i>. Then <i>d</i> must be the {@code double} value nearest
* to <i>x</i>; or if two {@code double} values are equally close
* to <i>x</i>, then <i>d</i> must be one of them and the least
* significant bit of the significand of <i>d</i> must be {@code 0}.
*
* <p>To create localized string representations of a floating-point
* value, use subclasses of {@link java.text.NumberFormat}.
Expand All @@ -321,7 +392,7 @@ public final class Double extends Number
* @return a string representation of the argument.
*/
public static String toString(double d) {
return FloatingDecimal.toJavaFormatString(d);
return DoubleToDecimal.toString(d);
}

/**
Expand Down
141 changes: 104 additions & 37 deletions src/java.base/share/classes/java/lang/Float.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.Optional;

import jdk.internal.math.FloatingDecimal;
import jdk.internal.math.FloatToDecimal;
import jdk.internal.vm.annotation.IntrinsicCandidate;

/**
Expand Down Expand Up @@ -189,53 +190,119 @@ public final class Float extends Number
* {@code "0.0"}; thus, negative zero produces the result
* {@code "-0.0"} and positive zero produces the result
* {@code "0.0"}.
* <li> If <i>m</i> is greater than or equal to 10<sup>-3</sup> but
* less than 10<sup>7</sup>, then it is represented as the
* integer part of <i>m</i>, in decimal form with no leading
* zeroes, followed by '{@code .}'
* ({@code '\u005Cu002E'}), followed by one or more
* decimal digits representing the fractional part of
* <i>m</i>.
* <li> If <i>m</i> is less than 10<sup>-3</sup> or greater than or
* equal to 10<sup>7</sup>, then it is represented in
* so-called "computerized scientific notation." Let <i>n</i>
* be the unique integer such that 10<sup><i>n</i> </sup>&le;
* <i>m</i> {@literal <} 10<sup><i>n</i>+1</sup>; then let <i>a</i>
* be the mathematically exact quotient of <i>m</i> and
* 10<sup><i>n</i></sup> so that 1 &le; <i>a</i> {@literal <} 10.
* The magnitude is then represented as the integer part of
* <i>a</i>, as a single decimal digit, followed by
* '{@code .}' ({@code '\u005Cu002E'}), followed by
* decimal digits representing the fractional part of
* <i>a</i>, followed by the letter '{@code E}'
* ({@code '\u005Cu0045'}), followed by a representation
* of <i>n</i> as a decimal integer, as produced by the
* method {@link java.lang.Integer#toString(int)}.
*
* <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 '.'} is {@code '\u005Cu002E'}
* and the exponent indicator {@code 'E'} 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>
* How many digits must be printed for the fractional part of
* <i>m</i> or <i>a</i>? There must be at least one digit
* to represent the fractional part, and beyond that as many, but
* only as many, more digits as are needed to uniquely distinguish
* the argument value from adjacent values of type
* {@code float}. That is, suppose that <i>x</i> is the
* exact mathematical value represented by the decimal
* representation produced by this method for a finite nonzero
* argument <i>f</i>. Then <i>f</i> must be the {@code float}
* value nearest to <i>x</i>; or, if two {@code float} values are
* equally close to <i>x</i>, then <i>f</i> must be one of
* them and the least significant bit of the significand of
* <i>f</i> must be {@code 0}.
*
* <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.
* @param f the {@code float} to be converted.
* @return a string representation of the argument.
*/
public static String toString(float f) {
return FloatingDecimal.toJavaFormatString(f);
return FloatToDecimal.toString(f);
}

/**
Expand Down
Loading

1 comment on commit 72bcf2a

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.