diff --git a/src/java.base/share/classes/java/math/BigDecimal.java b/src/java.base/share/classes/java/math/BigDecimal.java index dd34ad24571..207ff9bdf54 100644 --- a/src/java.base/share/classes/java/math/BigDecimal.java +++ b/src/java.base/share/classes/java/math/BigDecimal.java @@ -3503,21 +3503,19 @@ public class BigDecimal extends Number implements Comparable { return "0"; } int trailingZeros = checkScaleNonZero((-(long)scale)); - StringBuilder buf; - if(intCompact!=INFLATED) { - buf = new StringBuilder(20+trailingZeros); - buf.append(intCompact); - } else { - String str = intVal.toString(); - buf = new StringBuilder(str.length()+trailingZeros); - buf.append(str); - } - for (int i = 0; i < trailingZeros; i++) { - buf.append('0'); + String str = intCompact != INFLATED + ? Long.toString(intCompact) + : intVal.toString(); + int len = str.length() + trailingZeros; + if (len < 0) { + throw new OutOfMemoryError("too large to fit in a String"); } + StringBuilder buf = new StringBuilder(len); + buf.append(str); + buf.repeat('0', trailingZeros); return buf.toString(); } - String str ; + String str; if(intCompact!=INFLATED) { str = Long.toString(Math.abs(intCompact)); } else { @@ -3527,11 +3525,11 @@ public class BigDecimal extends Number implements Comparable { } /* Returns a digit.digit string */ - private String getValueString(int signum, String intString, int scale) { + private static String getValueString(int signum, String intString, int scale) { /* Insert decimal point */ StringBuilder buf; int insertionPoint = intString.length() - scale; - if (insertionPoint == 0) { /* Point goes right before intVal */ + if (insertionPoint == 0) { /* Point goes just before intVal */ return (signum<0 ? "-0." : "0.") + intString; } else if (insertionPoint > 0) { /* Point goes inside intVal */ buf = new StringBuilder(intString); @@ -3539,11 +3537,13 @@ public class BigDecimal extends Number implements Comparable { if (signum < 0) buf.insert(0, '-'); } else { /* We must insert zeros between point and intVal */ - buf = new StringBuilder(3-insertionPoint + intString.length()); - buf.append(signum<0 ? "-0." : "0."); - for (int i=0; i<-insertionPoint; i++) { - buf.append('0'); + int len = (signum < 0 ? 3 : 2) + scale; + if (len < 0) { + throw new OutOfMemoryError("too large to fit in a String"); } + buf = new StringBuilder(len); + buf.append(signum<0 ? "-0." : "0."); + buf.repeat('0', -insertionPoint); // insertionPoint != MIN_VALUE buf.append(intString); } return buf.toString(); diff --git a/test/jdk/java/math/BigDecimal/ToPlainStringTests.java b/test/jdk/java/math/BigDecimal/ToPlainStringTests.java index 7c7928ca85c..0a1f617c0e5 100644 --- a/test/jdk/java/math/BigDecimal/ToPlainStringTests.java +++ b/test/jdk/java/math/BigDecimal/ToPlainStringTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4984872 + * @bug 4984872 8318915 * @summary Basic tests of toPlainString method * @run main ToPlainStringTests * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+EliminateAutoBox -XX:AutoBoxCacheMax=20000 ToPlainStringTests @@ -67,6 +67,11 @@ public class ToPlainStringTests { {"12345678901234567890", "12345678901234567890"}, {"12345678901234567890e22", "123456789012345678900000000000000000000000"}, {"12345678901234567890e-22", "0.0012345678901234567890"}, + + {"12345e-1", "1234.5"}, + {"12345e-2", "123.45"}, + {"12345e-3", "12.345"}, + {"12345e-4", "1.2345"}, }; int errors = 0; @@ -89,6 +94,38 @@ public class ToPlainStringTests { } } + String[] failingCases = { + "1E-" + (Integer.MAX_VALUE - 0), // MAX_VALUE + 2 chars + "1E-" + (Integer.MAX_VALUE - 1), // MAX_VALUE + 1 chars + + "-1E-" + (Integer.MAX_VALUE - 0), // MAX_VALUE + 3 chars + "-1E-" + (Integer.MAX_VALUE - 1), // MAX_VALUE + 2 chars + "-1E-" + (Integer.MAX_VALUE - 2), // MAX_VALUE + 1 chars + + "123456789E-" + (Integer.MAX_VALUE - 0), // MAX_VALUE + 2 chars + "123456789E-" + (Integer.MAX_VALUE - 1), // MAX_VALUE + 1 chars + + "-123456789E-" + (Integer.MAX_VALUE - 0), // MAX_VALUE + 3 chars + "-123456789E-" + (Integer.MAX_VALUE - 1), // MAX_VALUE + 2 chars + "-123456789E-" + (Integer.MAX_VALUE - 2), // MAX_VALUE + 1 chars + + "1E" + Integer.MAX_VALUE, // MAX_VALUE + 1 chars + "123456789E" + Integer.MAX_VALUE, // MAX_VALUE + 9 chars + + "-1E" + Integer.MAX_VALUE, // MAX_VALUE + 2 chars + "-123456789E" + Integer.MAX_VALUE, // MAX_VALUE + 10 chars + }; + /* We expect pre-emptive OutOfMemoryErrors, nothing else */ + for (String failingCase : failingCases) { + try { + new BigDecimal(failingCase).toPlainString(); + } catch (OutOfMemoryError expected) { + continue; + } catch (Throwable ignored) { + } + ++errors; + } + if(errors > 0) throw new RuntimeException(errors + " errors during run."); }