diff --git a/src/share/classes/java/text/DigitList.java b/src/share/classes/java/text/DigitList.java index ce2a0685f9985073e857e7b851496170d797b488..da5e78d65b34ac4c617ca178c4ae75d573146fa9 100644 --- a/src/share/classes/java/text/DigitList.java +++ b/src/share/classes/java/text/DigitList.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, 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 @@ -41,6 +41,7 @@ package java.text; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; +import sun.misc.FloatingDecimal; /** * Digit List. Private to DecimalFormat. @@ -62,7 +63,7 @@ import java.math.RoundingMode; * derived by placing all the digits of the list to the right of the * decimal point, by 10^exponent. * - * @see java.util.Locale + * @see Locale * @see Format * @see NumberFormat * @see DecimalFormat @@ -286,14 +287,27 @@ final class DigitList implements Cloneable { * fractional digits to be converted. If false, total digits. */ final void set(boolean isNegative, double source, int maximumDigits, boolean fixedPoint) { - set(isNegative, Double.toString(source), maximumDigits, fixedPoint); + + FloatingDecimal fd = new FloatingDecimal(source); + boolean hasBeenRoundedUp = fd.digitsRoundedUp(); + boolean allDecimalDigits = fd.decimalDigitsExact(); + String digitsString = fd.toJavaFormatString(); + + set(isNegative, digitsString, + hasBeenRoundedUp, allDecimalDigits, + maximumDigits, fixedPoint); } /** * Generate a representation of the form DDDDD, DDDDD.DDDDD, or * DDDDDE+/-DDDDD. + * @param roundedUp Boolean value indicating if the s digits were rounded-up. + * @param allDecimalDigits Boolean value indicating if the digits in s are + * an exact decimal representation of the double that was passed. */ - final void set(boolean isNegative, String s, int maximumDigits, boolean fixedPoint) { + final void set(boolean isNegative, String s, + boolean roundedUp, boolean allDecimalDigits, + int maximumDigits, boolean fixedPoint) { this.isNegative = isNegative; int len = s.length(); char[] source = getDataChars(len); @@ -346,7 +360,7 @@ final class DigitList implements Cloneable { } else if (-decimalAt == maximumDigits) { // If we round 0.0009 to 3 fractional digits, then we have to // create a new one digit in the least significant location. - if (shouldRoundUp(0)) { + if (shouldRoundUp(0, roundedUp, allDecimalDigits)) { count = 1; ++decimalAt; digits[0] = '1'; @@ -365,19 +379,26 @@ final class DigitList implements Cloneable { // Eliminate digits beyond maximum digits to be displayed. // Round up if appropriate. - round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits); + round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits, + roundedUp, allDecimalDigits); } /** * Round the representation to the given number of digits. * @param maximumDigits The maximum number of digits to be shown. + * @param alreadyRounded Boolean indicating if rounding up already happened. + * @param allDecimalDigits Boolean indicating if the digits provide an exact + * representation of the value. + * * Upon return, count will be less than or equal to maximumDigits. */ - private final void round(int maximumDigits) { + private final void round(int maximumDigits, + boolean alreadyRounded, + boolean allDecimalDigits) { // Eliminate digits beyond maximum digits to be displayed. // Round up if appropriate. if (maximumDigits >= 0 && maximumDigits < count) { - if (shouldRoundUp(maximumDigits)) { + if (shouldRoundUp(maximumDigits, alreadyRounded, allDecimalDigits)) { // Rounding up involved incrementing digits from LSD to MSD. // In most cases this is simple, but in a worst case situation // (9999..99) we have to adjust the decimalAt value. @@ -423,8 +444,56 @@ final class DigitList implements Cloneable { * @return true if digit maximumDigits-1 should be * incremented */ - private boolean shouldRoundUp(int maximumDigits) { + private boolean shouldRoundUp(int maximumDigits, + boolean alreadyRounded, + boolean allDecimalDigits) { if (maximumDigits < count) { + /* + * To avoid erroneous double-rounding or truncation when converting + * a binary double value to text, information about the exactness + * of the conversion result in FloatingDecimal, as well as any + * rounding done, is needed in this class. + * + * - For the HALF_DOWN, HALF_EVEN, HALF_UP rounding rules below: + * In the case of formating float or double, We must take into + * account what FloatingDecimal has done in the binary to decimal + * conversion. + * + * Considering the tie cases, FloatingDecimal may round-up the + * value (returning decimal digits equal to tie when it is below), + * or "truncate" the value to the tie while value is above it, + * or provide the exact decimal digits when the binary value can be + * converted exactly to its decimal representation given formating + * rules of FloatingDecimal ( we have thus an exact decimal + * representation of the binary value). + * + * - If the double binary value was converted exactly as a decimal + * value, then DigitList code must apply the expected rounding + * rule. + * + * - If FloatingDecimal already rounded up the decimal value, + * DigitList should neither round up the value again in any of + * the three rounding modes above. + * + * - If FloatingDecimal has truncated the decimal value to + * an ending '5' digit, DigitList should round up the value in + * all of the three rounding modes above. + * + * + * This has to be considered only if digit at maximumDigits index + * is exactly the last one in the set of digits, otherwise there are + * remaining digits after that position and we dont have to consider + * what FloatingDecimal did. + * + * - Other rounding modes are not impacted by these tie cases. + * + * - For other numbers that are always converted to exact digits + * (like BigInteger, Long, ...), the passed alreadyRounded boolean + * have to be set to false, and allDecimalDigits has to be set to + * true in the upper DigitList call stack, providing the right state + * for those situations.. + */ + switch(roundingMode) { case UP: for (int i=maximumDigits; i= '5') { + // We should not round up if the rounding digits position is + // exactly the last index and if digits were already rounded. + if ((maximumDigits == (count - 1)) && + (alreadyRounded)) + return false; + + // Value was exactly at or was above tie. We must round up. return true; } break; @@ -458,6 +534,21 @@ final class DigitList implements Cloneable { if (digits[maximumDigits] > '5') { return true; } else if (digits[maximumDigits] == '5' ) { + if (maximumDigits == (count - 1)) { + // The rounding position is exactly the last index. + if (allDecimalDigits || alreadyRounded) + /* FloatingDecimal rounded up (value was below tie), + * or provided the exact list of digits (value was + * an exact tie). We should not round up, following + * the HALF_DOWN rounding rule. + */ + return false; + else + // Value was above the tie, we must round up. + return true; + } + + // We must round up if it gives a non null digit after '5'. for (int i=maximumDigits+1; i '5') { return true; } else if (digits[maximumDigits] == '5' ) { - for (int i=maximumDigits+1; i 0) && + (digits[maximumDigits-1] % 2 != 0)); + } + } else { + // Rounds up if it gives a non null digit after '5' + for (int i=maximumDigits+1; i 0 && (digits[maximumDigits-1] % 2 != 0); } break; case UNNECESSARY: @@ -542,7 +653,7 @@ final class DigitList implements Cloneable { count = right - left + 1; System.arraycopy(digits, left, digits, 0, count); } - if (maximumDigits > 0) round(maximumDigits); + if (maximumDigits > 0) round(maximumDigits, false, true); } /** @@ -559,7 +670,9 @@ final class DigitList implements Cloneable { String s = source.toString(); extendDigits(s.length()); - set(isNegative, s, maximumDigits, fixedPoint); + set(isNegative, s, + false, true, + maximumDigits, fixedPoint); } /** @@ -584,7 +697,7 @@ final class DigitList implements Cloneable { count = right + 1; if (maximumDigits > 0) { - round(maximumDigits); + round(maximumDigits, false, true); } } diff --git a/src/share/classes/sun/misc/FloatingDecimal.java b/src/share/classes/sun/misc/FloatingDecimal.java index 2679a4eec3ffd8e0a7691292c1501d6868b74353..2db646d2cc1cbe23bbffd14d9c59b908db4f6f67 100644 --- a/src/share/classes/sun/misc/FloatingDecimal.java +++ b/src/share/classes/sun/misc/FloatingDecimal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, 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 @@ -41,6 +41,19 @@ public class FloatingDecimal{ boolean fromHex = false; int roundDir = 0; // set by doubleValue + /* + * The fields below provides additional information about the result of + * the binary to decimal digits conversion done in dtoa() and roundup() + * methods. They are changed if needed by those two methods. + */ + + // True if the dtoa() binary to decimal conversion was exact. + boolean exactDecimalConversion = false; + + // True if the result of the binary to decimal conversion was rounded-up + // at the end of the conversion process, i.e. roundUp() method was called. + boolean decimalDigitsRoundedUp = false; + private FloatingDecimal( boolean negSign, int decExponent, char []digits, int n, boolean e ) { isNegative = negSign; @@ -396,6 +409,11 @@ public class FloatingDecimal{ // else fall through. } digits[i] = (char)(q+1); + decimalDigitsRoundedUp = true; + } + + public boolean digitsRoundedUp() { + return decimalDigitsRoundedUp; } /* @@ -751,6 +769,7 @@ public class FloatingDecimal{ digits[ndigit++] = (char)('0' + q); } lowDigitDifference = (b<<1) - tens; + exactDecimalConversion = (b == 0); } else { // still good! they're all longs! long b = (fractBits * long5pow[B5] ) << B2; @@ -804,8 +823,10 @@ public class FloatingDecimal{ digits[ndigit++] = (char)('0' + q); } lowDigitDifference = (b<<1) - tens; + exactDecimalConversion = (b == 0); } } else { + FDBigInt ZeroVal = new FDBigInt(0); FDBigInt tenSval; int shiftBias; @@ -859,8 +880,10 @@ public class FloatingDecimal{ if ( high && low ){ Bval.lshiftMe(1); lowDigitDifference = Bval.cmp(tenSval); - } else + } else { lowDigitDifference = 0L; // this here only for flow analysis! + } + exactDecimalConversion = (Bval.cmp( ZeroVal ) == 0); } this.decExponent = decExp+1; this.digits = digits; @@ -883,6 +906,10 @@ public class FloatingDecimal{ } } + public boolean decimalDigitsExact() { + return exactDecimalConversion; + } + public String toString(){ // most brain-dead version diff --git a/test/java/text/Format/DecimalFormat/TieRoundingTest.java b/test/java/text/Format/DecimalFormat/TieRoundingTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fb460db2e1f29ce5584f0b9a5c93cd7da263db58 --- /dev/null +++ b/test/java/text/Format/DecimalFormat/TieRoundingTest.java @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2012, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * + * @bug 7131459 + * @summary test various situations of NumberFormat rounding when close to tie + * @author Olivier Lagneau + * @run main TieRoundingTest + * + */ + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.NumberFormat; +import java.text.DecimalFormat; +import java.math.RoundingMode; +import java.util.Locale; + +public class TieRoundingTest { + + static int testCounter = 0; + static int errorCounter = 0; + static boolean allPassed = true; + + static void formatOutputTestDouble(NumberFormat nf, + double doubleToTest, + String tiePosition, + String inputDigits, + String expectedOutput) { + + int mfd = nf.getMaximumFractionDigits(); + RoundingMode rm = nf.getRoundingMode(); + String result = nf.format(doubleToTest); + + if (!result.equals(expectedOutput)) { + System.out.println(); + System.out.println("========================================"); + System.out.println("***Error formatting double value from string : " + + inputDigits); + System.out.println("NumberFormat pattern is : " + + ((DecimalFormat ) nf).toPattern()); + System.out.println("Maximum number of fractional digits : " + mfd); + System.out.println("Fractional rounding digit : " + (mfd + 1)); + System.out.println("Position of value relative to tie : " + tiePosition); + System.out.println("Rounding Mode : " + rm); + System.out.println("BigDecimal output : " + + new BigDecimal(doubleToTest).toString()); + System.out.println("FloatingDecimal output : " + doubleToTest); + System.out.println( + "Error. Formatted result different from expected." + + "\nExpected output is : \"" + expectedOutput + "\"" + + "\nFormated output is : \"" + result + "\""); + System.out.println("========================================"); + System.out.println(); + + errorCounter++; + allPassed = false; + } else { + testCounter++; + System.out.println("\nSuccess for double value : " + doubleToTest + " :"); + System.out.println(" Input digits :" + inputDigits + + ", BigDecimal value : " + + new BigDecimal(doubleToTest).toString()); + System.out.print(" Rounding mode: " + rm); + System.out.print(", fract digits : " + mfd); + System.out.print(", position : " + tiePosition + " tie"); + System.out.print(", result : " + result); + System.out.println(", expected : " + expectedOutput); + } + } + + static void formatOutputTestLong(NumberFormat nf, + long longToTest, + String tiePosition, + String inputDigits, + String expectedOutput) { + + int mfd = nf.getMaximumFractionDigits(); + RoundingMode rm = nf.getRoundingMode(); + String result = nf.format(longToTest); + + if (!result.equals(expectedOutput)) { + System.out.println(); + System.out.println("========================================"); + System.out.println("***Error formatting double value from string : " + + inputDigits); + System.out.println("NumberFormat pattern is : " + + ((DecimalFormat ) nf).toPattern()); + System.out.println("Maximum number of fractional digits : " + mfd); + System.out.println("Fractional rounding digit : " + (mfd + 1)); + System.out.println("Position of value relative to tie : " + tiePosition); + System.out.println("Rounding Mode : " + rm); + System.out.println( + "Error. Formatted result different from expected." + + "\nExpected output is : \"" + expectedOutput + "\"" + + "\nFormated output is : \"" + result + "\""); + System.out.println("========================================"); + System.out.println(); + + errorCounter++; + allPassed = false; + } else { + testCounter++; + System.out.print("Success. Long input :" + inputDigits); + System.out.print(", rounding : " + rm); + System.out.print(", fract digits : " + mfd); + System.out.print(", tie position : " + tiePosition); + System.out.println(", expected : " + expectedOutput); + + } + } + + static void formatOutputTestObject(NumberFormat nf, + Object someNumber, + String tiePosition, + String inputDigits, + String expectedOutput) { + + int mfd = nf.getMaximumFractionDigits(); + RoundingMode rm = nf.getRoundingMode(); + String result = nf.format(someNumber); + + if (!result.equals(expectedOutput)) { + System.out.println(); + System.out.println("========================================"); + System.out.println("***Error formatting number value from string : " + + inputDigits); + System.out.println("NumberFormat pattern is : " + + ((DecimalFormat ) nf).toPattern()); + System.out.println("Maximum number of fractional digits : " + mfd); + System.out.println("Fractional rounding digit : " + (mfd + 1)); + System.out.println("Position of value relative to tie : " + tiePosition); + System.out.println("Rounding Mode : " + rm); + System.out.println("Number self output representation: " + someNumber); + System.out.println( + "Error. Formatted result different from expected." + + "\nExpected output is : \"" + expectedOutput + "\"" + + "\nFormated output is : \"" + result + "\""); + System.out.println("========================================"); + System.out.println(); + + errorCounter++; + allPassed = false; + } else { + testCounter++; + System.out.print("Success. Number input :" + inputDigits); + System.out.print(", rounding : " + rm); + System.out.print(", fract digits : " + mfd); + System.out.print(", tie position : " + tiePosition); + System.out.println(", expected : " + expectedOutput); + } + } + + public static void main(String[] args) { + + // Only the 3 rounding modes below may be impacted by bug 7131459. + // So we do not test the other rounding modes. + RoundingMode[] roundingModes = { + RoundingMode.HALF_DOWN, + RoundingMode.HALF_EVEN, + RoundingMode.HALF_UP + }; + + // Precise the relative position of input value against its closest tie. + String[] tieRelativePositions = { + "below", "exact", "above", + "below", "exact", "above", + "below", "exact", "above", + "below", "exact", "above" + }; + + // =============== Testing double (and thus float) value cases ========= + + System.out.println("\n===== testing 3 digits rounding position ====="); + double[] values3FractDigits = { + // unimpacting values close to tie, with less than 3 input fract digits + 1.115d, 1.125d, 1.135d, + // impacting close to tie values covering all 6 cases + 0.3115d, 0.3125d, 0.3135d, + 0.6865d, 0.6875d, 0.6885d, + // unimpacting values close to tie, with more than 3 input fract digits + 1.46885d, 2.46875d, 1.46865d + }; + + String[] inputs3FractDigits = { + "1.115d", "1.125d", "1.135d", + "0.3115d", "0.3125d", "0.3135d", + "0.6865d", "0.6875d", "0.6885d", + "1.46885d", "2.46875d", "1.46865d" + }; + + String[][] expected3FractDigits = { + {"1.115", "1.125", "1.135", + "0.311", "0.312", "0.314", + "0.686", "0.687", "0.689", + "1.469", "2.469", "1.469" + }, + {"1.115", "1.125", "1.135", + "0.311", "0.312", "0.314", + "0.686", "0.688", "0.689", + "1.469", "2.469", "1.469" + }, + {"1.115", "1.125", "1.135", + "0.311", "0.313", "0.314", + "0.686", "0.688", "0.689", + "1.469", "2.469", "1.469" + }, + }; + + + for (int r = 0; r < roundingModes.length; r++) { + NumberFormat dfDefault = NumberFormat.getInstance(Locale.US); + RoundingMode rmode = roundingModes[r]; + dfDefault.setRoundingMode(rmode); + System.out.println("\n----- Now checking " + rmode + + " rounding mode -----"); + + for (int i = 0; i < values3FractDigits.length; i++) { + double d = values3FractDigits[i]; + String tiePosition = tieRelativePositions[i]; + String input = inputs3FractDigits[i]; + String expected = expected3FractDigits[r][i]; + + formatOutputTestDouble(dfDefault, d, tiePosition, input, expected); + } + } + + System.out.println("\n===== testing 5 digits rounding position ====="); + double[] values5FractDigits = { + // unimpacting values close to tie, with less than 5 input fract digits + 1.3135d, 1.3125d, 1.3115d, + // impacting values close to tie, covering all 6 cases + 1.328115d, 1.328125d, 1.328135d, + 1.796865d, 1.796875d, 1.796885d, + // unimpacting values close to tie, with more than 5 input fract digits + 1.3281149999999d, 1.75390625d, 1.7968750000001d + }; + + String[] inputs5FractDigits = { + "1.3135d", "1.3125d", "1.3115d", + "1.328115d", "1.328125d", "1.328135d", + "1.796865d", "1.796875d", "1.796885d", + "1.3281149999999d", "1.75390625d", "1.7968750000001d" + }; + + String[][] expected5FractDigits = { + {"1.3135", "1.3125", "1.3115", + "1.32811", "1.32812", "1.32814", + "1.79686", "1.79687", "1.79689", + "1.32811", "1.75391", "1.79688" + }, + {"1.3135", "1.3125", "1.3115", + "1.32811", "1.32812", "1.32814", + "1.79686", "1.79688", "1.79689", + "1.32811", "1.75391", "1.79688" + }, + {"1.3135", "1.3125", "1.3115", + "1.32811", "1.32813", "1.32814", + "1.79686", "1.79688", "1.79689", + "1.32811", "1.75391", "1.79688" + } + }; + + + for (int r = 0; r < roundingModes.length; r++) { + DecimalFormat df5 = (DecimalFormat) NumberFormat.getInstance(Locale.US); + RoundingMode rmode = roundingModes[r]; + df5.setRoundingMode(rmode); + System.out.println("\n----- Now checking " + rmode + + " rounding mode -----"); + df5.applyPattern("#,###.#####"); + + for (int i = 0; i < values5FractDigits.length; i++) { + double d = values5FractDigits[i]; + String tiePosition = tieRelativePositions[i]; + String input = inputs5FractDigits[i]; + String expected = expected5FractDigits[r][i]; + + formatOutputTestDouble(df5, d, tiePosition, input, expected); + } + } + + // ==================== Testing long value cases ==================== + + System.out.println("\n===== testing long values ====="); + long l = 123456789012345L; + DecimalFormat dfLong = (DecimalFormat) NumberFormat.getInstance(Locale.US); + String tiePosition = "exact"; + String input = "123456789012345L"; + String expected = "123,456,789,012,345"; + String result = dfLong.format(l); + formatOutputTestLong(dfLong, l, tiePosition, input, expected); + + dfLong.applyPattern("0.###E0"); + expected = "1.235E14"; + formatOutputTestLong(dfLong, l, tiePosition, input, expected); + + l = 123450000000000L; + input = "123450000000000L"; + expected = "1.234E14"; + formatOutputTestLong(dfLong, l, tiePosition, input, expected); + + l = 987750000000000L; + input = "987750000000000L"; + expected = "9.878E14"; + formatOutputTestLong(dfLong, l, tiePosition, input, expected); + + dfLong.applyPattern("#,###.0E0"); + l = 987755000000000L; + input = "987755000000000L"; + expected = "987.76E12"; + + formatOutputTestLong(dfLong, l, tiePosition, input, expected); + + + // ================= Testing BigInteger value cases ================= + + System.out.println("\n===== testing BigInteger values ====="); + String stringValue = "12345678901234567890123456789012345"; + BigInteger bi = new BigInteger(stringValue); + DecimalFormat dfBig = (DecimalFormat) NumberFormat.getInstance(Locale.US); + tiePosition = "exact"; + input = stringValue; + expected = "12,345,678,901,234,567,890,123,456,789,012,345"; + formatOutputTestObject(dfBig, bi, tiePosition, input, expected); + + dfBig.applyPattern("0.###E0"); + expected = "1.235E34"; + formatOutputTestObject(dfBig, bi, tiePosition, input, expected); + + stringValue = "12345000000000000000000000000000000"; + input = stringValue; + bi = new BigInteger(stringValue); + expected = "1.234E34"; + formatOutputTestObject(dfBig, bi, tiePosition, input, expected); + + stringValue = "12345000000000000000000000000000001"; + input = stringValue; + bi = new BigInteger(stringValue); + expected = "1.235E34"; + formatOutputTestObject(dfBig, bi, tiePosition, input, expected); + + stringValue = "98755000000000000000000000000000000"; + input = stringValue; + bi = new BigInteger(stringValue); + expected = "9.876E34"; + formatOutputTestObject(dfBig, bi, tiePosition, input, expected); + + dfLong.applyPattern("#,###.0E0"); + stringValue = "98775500000000000000000000000000000"; + input = stringValue; + expected = "987.76E34"; + + // =============== Testing BigDecimal value cases ================ + + System.out.println("\n===== testing BigDecimal values ====="); + dfBig = (DecimalFormat) NumberFormat.getInstance(Locale.US); + + stringValue = "0.68850000000000000088817841970012523233890533447265625"; + BigDecimal bd = new BigDecimal(stringValue); + tiePosition = "exact"; + input = stringValue; + expected = "0.689"; + formatOutputTestObject(dfBig, bd, tiePosition, input, expected); + + stringValue = "0.31149999999999999911182158029987476766109466552734375"; + bd = new BigDecimal(stringValue); + dfBig.applyPattern("#,##0.####"); + tiePosition = "exact"; + input = stringValue; + expected = "0.3115"; + formatOutputTestObject(dfBig, bd, tiePosition, input, expected); + + // ==================== Printing results and exiting =================== + + System.out.println(); + System.out.println("==> " + testCounter + " tests passed successfully"); + System.out.println("==> " + errorCounter + " tests failed"); + + System.out.println(); + if (allPassed) { + System.out.println( + "Success in formating all the values with the given parameters"); + } else { + String s = "Test failed with " + errorCounter + " formating error(s)."; + System.out.println(s); + throw new RuntimeException(s); + } + } +}