test/java/lang/Double/ParseDouble.java

Print this page
rev 7487 : 7192954: Fix Float.parseFloat to round correctly and preserve monotonicity.
4396272: Parsing doubles fails to follow IEEE for largest decimal that should yield 0
7039391: Use Math.ulp in FloatingDecimal
Summary: Correct rounding and monotonicity problems in floats and doubles
Reviewed-by: martin
Contributed-by: Dmitry Nadezhin <dmitry.nadezhin@oracle.com>, Louis Wasserman <lowasser@google.com>


   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 4160406 4705734 4707389 4826774 4895911 4421494 7021568 7039369
  27  * @summary Test for Double.parseDouble method and acceptance regex
  28  */
  29 
  30 import java.util.regex.*;
  31 import java.math.BigDecimal;


  32 
  33 public class ParseDouble {
  34 





















































































  35     private static void check(String val, double expected) {
  36         double n = Double.parseDouble(val);
  37         if (n != expected)
  38             throw new RuntimeException("Double.parseDouble failed. String:" +
  39                                                 val + " Result:" + n);
  40     }
  41 
  42     private static void rudimentaryTest() {
  43         check(new String(""+Double.MIN_VALUE), Double.MIN_VALUE);
  44         check(new String(""+Double.MAX_VALUE), Double.MAX_VALUE);
  45 
  46         check("10",     (double)  10.0);
  47         check("10.0",   (double)  10.0);
  48         check("10.01",  (double)  10.01);
  49 
  50         check("-10",    (double) -10.0);
  51         check("-10.00", (double) -10.0);
  52         check("-10.01", (double) -10.01);
  53     }
  54 
  55 
  56     static  String badStrings[] = {
  57         "",
  58         "+",
  59         "-",


 443     }
 444 
 445 
 446     /*
 447      * Throws an exception if <code>Input</code> is
 448      * <code>exceptionalInput</code> and {@link Double.parseDouble
 449      * parseDouble} does <em>not</em> throw an exception or if
 450      * <code>Input</code> is not <code>exceptionalInput</code> and
 451      * <code>parseDouble</code> throws an exception.  This method does
 452      * not attempt to test whether the string is converted to the
 453      * proper value; just whether the input is accepted appropriately
 454      * or not.
 455      */
 456     private static void testParsing(String [] input,
 457                                     boolean exceptionalInput) {
 458         for(int i = 0; i < input.length; i++) {
 459             double d;
 460 
 461             try {
 462                 d = Double.parseDouble(input[i]);

 463             }
 464             catch (NumberFormatException e) {
 465                 if (! exceptionalInput) {
 466                     throw new RuntimeException("Double.parseDouble rejected " +
 467                                                "good string `" + input[i] +
 468                                                "'.");
 469                 }
 470                 break;
 471             }
 472             if (exceptionalInput) {
 473                 throw new RuntimeException("Double.parseDouble accepted " +
 474                                            "bad string `" + input[i] +
 475                                            "'.");
 476             }
 477         }
 478     }
 479 
 480     /*
 481      * Throws an exception if <code>Input</code> is
 482      * <code>exceptionalInput</code> and the regular expression


 543 
 544         for(int i = 0; i < input.length; i++) {
 545              Matcher m = fpPattern.matcher(input[i]);
 546              if (m.matches() != ! exceptionalInput) {
 547                  throw new RuntimeException("Regular expression " +
 548                                             (exceptionalInput?
 549                                              "accepted bad":
 550                                              "rejected good") +
 551                                             " string `" +
 552                                             input[i] + "'.");
 553              }
 554         }
 555 
 556     }
 557 
 558     /**
 559      * For each subnormal power of two, test at boundaries of
 560      * region that should convert to that value.
 561      */
 562     private static void testSubnormalPowers() {

 563         BigDecimal TWO = BigDecimal.valueOf(2);
 564         // An ulp is the same for all subnormal values
 565         BigDecimal ulp_BD = new BigDecimal(Double.MIN_VALUE);
 566 
 567         // Test subnormal powers of two
 568         for(int i = -1074; i <= -1022; i++) {
 569             double d = Math.scalb(1.0, i);
 570 
 571             /*
 572              * The region [d - ulp/2, d + ulp/2] should round to d.
 573              */
 574             BigDecimal d_BD = new BigDecimal(d);
 575 
 576             BigDecimal lowerBound = d_BD.subtract(ulp_BD.divide(TWO));
 577             BigDecimal upperBound = d_BD.add(ulp_BD.divide(TWO));
 578 
 579             double convertedLowerBound = Double.parseDouble(lowerBound.toString());
 580             double convertedUpperBound = Double.parseDouble(upperBound.toString());









 581         }
 582     }





















 583 





















 584 
 585     private static void testStrictness() {
 586         final double expected = 0x0.0000008000001p-1022;

 587         boolean failed = false;
 588         double conversion = 0.0;
 589         double sum = 0.0; // Prevent conversion from being optimized away
 590 
 591         //2^-1047 + 2^-1075
 592         String decimal = "6.631236871469758276785396630275967243399099947355303144249971758736286630139265439618068200788048744105960420552601852889715006376325666595539603330361800519107591783233358492337208057849499360899425128640718856616503093444922854759159988160304439909868291973931426625698663157749836252274523485312442358651207051292453083278116143932569727918709786004497872322193856150225415211997283078496319412124640111777216148110752815101775295719811974338451936095907419622417538473679495148632480391435931767981122396703443803335529756003353209830071832230689201383015598792184172909927924176339315507402234836120730914783168400715462440053817592702766213559042115986763819482654128770595766806872783349146967171293949598850675682115696218943412532098591327667236328125E-316";
 593 
 594         for(int i = 0; i <= 12_000; i++) {
 595             conversion = Double.parseDouble(decimal);
 596             sum += conversion;
 597             if (conversion != expected) {
 598                 failed = true;
 599                 System.out.printf("Iteration %d converts as %a%n",
 600                                   i, conversion);
 601             }
 602         }
 603 
 604         System.out.println("Sum = "  + sum);
 605         if (failed)
 606             throw new RuntimeException("Inconsistent conversion");
 607     }
 608 
 609     public static void main(String[] args) throws Exception {
 610         rudimentaryTest();
 611 
 612         testParsing(goodStrings, false);
 613         testParsing(paddedGoodStrings, false);
 614         testParsing(badStrings, true);
 615         testParsing(paddedBadStrings, true);
 616 
 617         testRegex(goodStrings, false);
 618         testRegex(paddedGoodStrings, false);
 619         testRegex(badStrings, true);
 620         testRegex(paddedBadStrings, true);
 621 
 622         testSubnormalPowers();

 623         testStrictness();
 624     }
 625 }


   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 4160406 4705734 4707389 4826774 4895911 4421494 6358355 7021568 7039369 4396272
  27  * @summary Test for Double.parseDouble method and acceptance regex
  28  */
  29 

  30 import java.math.BigDecimal;
  31 import java.math.BigInteger;
  32 import java.util.regex.*;
  33 
  34 public class ParseDouble {
  35 
  36     private static final BigDecimal HALF = BigDecimal.valueOf(0.5);
  37 
  38     private static void fail(String val, double n) {
  39         throw new RuntimeException("Double.parseDouble failed. String:" +
  40                                                 val + " Result:" + n);
  41     }
  42 
  43     private static void check(String val) {
  44         double n = Double.parseDouble(val);
  45         boolean isNegativeN = n < 0 || n == 0 && 1/n < 0;
  46         double na = Math.abs(n);
  47         String s = val.trim().toLowerCase();
  48         switch (s.charAt(s.length() - 1)) {
  49             case 'd':
  50             case 'f':
  51                 s = s.substring(0, s.length() - 1);
  52                 break;
  53         }
  54         boolean isNegative = false;
  55         if (s.charAt(0) == '+') {
  56             s = s.substring(1);
  57         } else if (s.charAt(0) == '-') {
  58             s = s.substring(1);
  59             isNegative = true;
  60         }
  61         if (s.equals("nan")) {
  62             if (!Double.isNaN(n)) {
  63                 fail(val, n);
  64             }
  65             return;
  66         }
  67         if (Double.isNaN(n)) {
  68             fail(val, n);
  69         }
  70         if (isNegativeN != isNegative)
  71             fail(val, n);
  72         if (s.equals("infinity")) {
  73             if (na != Double.POSITIVE_INFINITY) {
  74                 fail(val, n);
  75             }
  76             return;
  77         }
  78         BigDecimal bd;
  79         if (s.startsWith("0x")) {
  80             s = s.substring(2);
  81             int indP = s.indexOf('p');
  82             long exp = Long.parseLong(s.substring(indP + 1));
  83             int indD = s.indexOf('.');
  84             String significand;
  85             if (indD >= 0) {
  86                 significand = s.substring(0, indD) + s.substring(indD + 1, indP);
  87                 exp -= 4*(indP - indD - 1);
  88             } else {
  89                 significand = s.substring(0, indP);
  90             }
  91             bd = new BigDecimal(new BigInteger(significand, 16));
  92             if (exp >= 0) {
  93                 bd = bd.multiply(BigDecimal.valueOf(2).pow((int)exp));
  94             } else {
  95                 bd = bd.divide(BigDecimal.valueOf(2).pow((int)-exp));
  96             }
  97         } else {
  98             bd = new BigDecimal(s);
  99         }
 100         BigDecimal l, u;
 101         if (Double.isInfinite(na)) {
 102             l = new BigDecimal(Double.MAX_VALUE).add(new BigDecimal(Math.ulp(Double.MAX_VALUE)).multiply(HALF));
 103             u = null;
 104         } else {
 105             l = new BigDecimal(na).subtract(new BigDecimal(Math.ulp(Math.nextUp(-na))).multiply(HALF));
 106             u = new BigDecimal(na).add(new BigDecimal(Math.ulp(n)).multiply(HALF));
 107         }
 108         int cmpL = bd.compareTo(l);
 109         int cmpU = u != null ? bd.compareTo(u) : -1;
 110         if ((Double.doubleToLongBits(n) & 1) != 0) {
 111             if (cmpL <= 0 || cmpU >= 0) {
 112                 fail(val, n);
 113             }
 114         } else {
 115             if (cmpL < 0 || cmpU > 0) {
 116                 fail(val, n);
 117             }
 118         }
 119     }
 120 
 121     private static void check(String val, double expected) {
 122         double n = Double.parseDouble(val);
 123         if (n != expected)
 124             fail(val, n);
 125         check(val);
 126     }
 127 
 128     private static void rudimentaryTest() {
 129         check(new String(""+Double.MIN_VALUE), Double.MIN_VALUE);
 130         check(new String(""+Double.MAX_VALUE), Double.MAX_VALUE);
 131 
 132         check("10",     (double)  10.0);
 133         check("10.0",   (double)  10.0);
 134         check("10.01",  (double)  10.01);
 135 
 136         check("-10",    (double) -10.0);
 137         check("-10.00", (double) -10.0);
 138         check("-10.01", (double) -10.01);
 139     }
 140 
 141 
 142     static  String badStrings[] = {
 143         "",
 144         "+",
 145         "-",


 529     }
 530 
 531 
 532     /*
 533      * Throws an exception if <code>Input</code> is
 534      * <code>exceptionalInput</code> and {@link Double.parseDouble
 535      * parseDouble} does <em>not</em> throw an exception or if
 536      * <code>Input</code> is not <code>exceptionalInput</code> and
 537      * <code>parseDouble</code> throws an exception.  This method does
 538      * not attempt to test whether the string is converted to the
 539      * proper value; just whether the input is accepted appropriately
 540      * or not.
 541      */
 542     private static void testParsing(String [] input,
 543                                     boolean exceptionalInput) {
 544         for(int i = 0; i < input.length; i++) {
 545             double d;
 546 
 547             try {
 548                 d = Double.parseDouble(input[i]);
 549                 check(input[i]);
 550             }
 551             catch (NumberFormatException e) {
 552                 if (! exceptionalInput) {
 553                     throw new RuntimeException("Double.parseDouble rejected " +
 554                                                "good string `" + input[i] +
 555                                                "'.");
 556                 }
 557                 break;
 558             }
 559             if (exceptionalInput) {
 560                 throw new RuntimeException("Double.parseDouble accepted " +
 561                                            "bad string `" + input[i] +
 562                                            "'.");
 563             }
 564         }
 565     }
 566 
 567     /*
 568      * Throws an exception if <code>Input</code> is
 569      * <code>exceptionalInput</code> and the regular expression


 630 
 631         for(int i = 0; i < input.length; i++) {
 632              Matcher m = fpPattern.matcher(input[i]);
 633              if (m.matches() != ! exceptionalInput) {
 634                  throw new RuntimeException("Regular expression " +
 635                                             (exceptionalInput?
 636                                              "accepted bad":
 637                                              "rejected good") +
 638                                             " string `" +
 639                                             input[i] + "'.");
 640              }
 641         }
 642 
 643     }
 644 
 645     /**
 646      * For each subnormal power of two, test at boundaries of
 647      * region that should convert to that value.
 648      */
 649     private static void testSubnormalPowers() {
 650         boolean failed = false;
 651         BigDecimal TWO = BigDecimal.valueOf(2);
 652         // An ulp is the same for all subnormal values
 653         BigDecimal ulp_BD = new BigDecimal(Double.MIN_VALUE);
 654 
 655         // Test subnormal powers of two (except Double.MIN_VALUE)
 656         for(int i = -1073; i <= -1022; i++) {
 657             double d = Math.scalb(1.0, i);
 658 
 659             /*
 660              * The region [d - ulp/2, d + ulp/2] should round to d.
 661              */
 662             BigDecimal d_BD = new BigDecimal(d);
 663 
 664             BigDecimal lowerBound = d_BD.subtract(ulp_BD.divide(TWO));
 665             BigDecimal upperBound = d_BD.add(ulp_BD.divide(TWO));
 666 
 667             double convertedLowerBound = Double.parseDouble(lowerBound.toString());
 668             double convertedUpperBound = Double.parseDouble(upperBound.toString());
 669             if (convertedLowerBound != d) {
 670                 failed = true;
 671                 System.out.printf("2^%d lowerBound converts as %a %s%n",
 672                                   i, convertedLowerBound, lowerBound);
 673             }
 674             if (convertedUpperBound != d) {
 675                 failed = true;
 676                 System.out.printf("2^%d upperBound converts as %a %s%n",
 677                                   i, convertedUpperBound, upperBound);
 678             }
 679         }
 680         /*
 681          * Double.MIN_VALUE
 682          * The region ]0.5*Double.MIN_VALUE, 1.5*Double.MIN_VALUE[ should round to Double.MIN_VALUE .
 683          */
 684         BigDecimal minValue = new BigDecimal(Double.MIN_VALUE);
 685         if (Double.parseDouble(minValue.multiply(new BigDecimal(0.5)).toString()) != 0.0) {
 686             failed = true;
 687             System.out.printf("0.5*MIN_VALUE doesn't convert 0%n");
 688         }
 689         if (Double.parseDouble(minValue.multiply(new BigDecimal(0.50000000001)).toString()) != Double.MIN_VALUE) {
 690             failed = true;
 691             System.out.printf("0.50000000001*MIN_VALUE doesn't convert to MIN_VALUE%n");
 692         }
 693         if (Double.parseDouble(minValue.multiply(new BigDecimal(1.49999999999)).toString()) != Double.MIN_VALUE) {
 694             failed = true;
 695             System.out.printf("1.49999999999*MIN_VALUE doesn't convert to MIN_VALUE%n");
 696         }
 697         if (Double.parseDouble(minValue.multiply(new BigDecimal(1.5)).toString()) != 2*Double.MIN_VALUE) {
 698             failed = true;
 699             System.out.printf("1.5*MIN_VALUE doesn't convert to 2*MIN_VALUE%n");
 700         }
 701 
 702         if (failed)
 703             throw new RuntimeException("Inconsistent conversion");
 704     }
 705 
 706     /**
 707      * For each power of two, test at boundaries of
 708      * region that should convert to that value.
 709      */
 710     private static void testPowers() {
 711         for(int i = -1074; i <= +1023; i++) {
 712             double d = Math.scalb(1.0, i);
 713             BigDecimal d_BD = new BigDecimal(d);
 714 
 715             BigDecimal lowerBound = d_BD.subtract(new BigDecimal(Math.ulp(Math.nextUp(-d))).multiply(HALF));
 716             BigDecimal upperBound = d_BD.add(new BigDecimal(Math.ulp(d)).multiply(HALF));
 717 
 718             check(lowerBound.toString());
 719             check(upperBound.toString());
 720         }
 721         check(new BigDecimal(Double.MAX_VALUE).add(new BigDecimal(Math.ulp(Double.MAX_VALUE)).multiply(HALF)).toString());
 722     }
 723 
 724     private static void testStrictness() {
 725         final double expected = 0x0.0000008000000p-1022;
 726 //        final double expected = 0x0.0000008000001p-1022;
 727         boolean failed = false;
 728         double conversion = 0.0;
 729         double sum = 0.0; // Prevent conversion from being optimized away
 730 
 731         //2^-1047 + 2^-1075 rounds to 2^-1047
 732         String decimal = "6.631236871469758276785396630275967243399099947355303144249971758736286630139265439618068200788048744105960420552601852889715006376325666595539603330361800519107591783233358492337208057849499360899425128640718856616503093444922854759159988160304439909868291973931426625698663157749836252274523485312442358651207051292453083278116143932569727918709786004497872322193856150225415211997283078496319412124640111777216148110752815101775295719811974338451936095907419622417538473679495148632480391435931767981122396703443803335529756003353209830071832230689201383015598792184172909927924176339315507402234836120730914783168400715462440053817592702766213559042115986763819482654128770595766806872783349146967171293949598850675682115696218943412532098591327667236328125E-316";
 733 
 734         for(int i = 0; i <= 12_000; i++) {
 735             conversion = Double.parseDouble(decimal);
 736             sum += conversion;
 737             if (conversion != expected) {
 738                 failed = true;
 739                 System.out.printf("Iteration %d converts as %a%n",
 740                                   i, conversion);
 741             }
 742         }
 743 
 744         System.out.println("Sum = "  + sum);
 745         if (failed)
 746             throw new RuntimeException("Inconsistent conversion");
 747     }
 748 
 749     public static void main(String[] args) throws Exception {
 750         rudimentaryTest();
 751 
 752         testParsing(goodStrings, false);
 753         testParsing(paddedGoodStrings, false);
 754         testParsing(badStrings, true);
 755         testParsing(paddedBadStrings, true);
 756 
 757         testRegex(goodStrings, false);
 758         testRegex(paddedGoodStrings, false);
 759         testRegex(badStrings, true);
 760         testRegex(paddedBadStrings, true);
 761 
 762         testSubnormalPowers();
 763         testPowers();
 764         testStrictness();
 765     }
 766 }