1 /* 2 * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 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 * @summary round trip test NumberFormat 27 * @library /java/text/testlib 28 * @key randomness 29 */ 30 31 import java.text.*; 32 import java.util.*; 33 34 /** 35 * This class tests the round-trip behavior of NumberFormat, DecimalFormat, and DigitList. 36 * Round-trip behavior is tested by taking a numeric value and formatting it, then 37 * parsing the resulting string, and comparing this result with the original value. 38 * Two tests are applied: String preservation, and numeric preservation. String 39 * preservation is exact; numeric preservation is not. However, numeric preservation 40 * should extend to the few least-significant bits. 41 * //bug472 42 */ 43 public class NumberRoundTrip extends IntlTest { 44 static final boolean STRING_COMPARE = true; 45 static final boolean EXACT_NUMERIC_COMPARE = false; 46 static final double MAX_ERROR = 1e-14; 47 static boolean DEBUG = false; 48 static double max_numeric_error = 0; 49 static double min_numeric_error = 1; 50 51 String localeName, formatName; 52 main(String[] args)53 public static void main(String[] args) throws Exception { 54 if (args.length > 0 && args[0].equals("-debug")) { 55 DEBUG = true; 56 String[] newargs = new String[args.length - 1]; 57 System.arraycopy(args, 1, newargs, 0, newargs.length); 58 args = newargs; 59 } 60 new NumberRoundTrip().run(args); 61 } 62 TestNumberFormatRoundTrip()63 public void TestNumberFormatRoundTrip() { 64 logln("Default Locale"); 65 localeName = "Default Locale"; 66 formatName = "getInstance"; 67 doTest(NumberFormat.getInstance()); 68 formatName = "getNumberInstance"; 69 doTest(NumberFormat.getNumberInstance()); 70 formatName = "getCurrencyInstance"; 71 doTest(NumberFormat.getCurrencyInstance()); 72 formatName = "getPercentInstance"; 73 doTest(NumberFormat.getPercentInstance()); 74 75 Locale[] loc = NumberFormat.getAvailableLocales(); 76 for (int i=0; i<loc.length; ++i) { 77 logln(loc[i].getDisplayName()); 78 localeName = loc[i].toString(); 79 formatName = "getInstance"; 80 doTest(NumberFormat.getInstance(loc[i])); 81 formatName = "getNumberInstance"; 82 doTest(NumberFormat.getNumberInstance(loc[i])); 83 formatName = "getCurrencyInstance"; 84 doTest(NumberFormat.getCurrencyInstance(loc[i])); 85 formatName = "getPercentInstance"; 86 doTest(NumberFormat.getPercentInstance(loc[i])); 87 } 88 89 logln("Numeric error " + 90 min_numeric_error + " to " + 91 max_numeric_error); 92 } 93 doTest(NumberFormat fmt)94 public void doTest(NumberFormat fmt) { 95 doTest(fmt, Double.NaN); 96 doTest(fmt, Double.POSITIVE_INFINITY); 97 doTest(fmt, Double.NEGATIVE_INFINITY); 98 99 doTest(fmt, 500); 100 doTest(fmt, 0); 101 doTest(fmt, 5555555555555555L); 102 doTest(fmt, 55555555555555555L); 103 doTest(fmt, 9223372036854775807L); 104 doTest(fmt, 9223372036854775808.0); 105 doTest(fmt, -9223372036854775808L); 106 doTest(fmt, -9223372036854775809.0); 107 108 for (int i=0; i<2; ++i) { 109 doTest(fmt, randomDouble(1)); 110 doTest(fmt, randomDouble(10000)); 111 doTest(fmt, Math.floor(randomDouble(10000))); 112 doTest(fmt, randomDouble(1e50)); 113 doTest(fmt, randomDouble(1e-50)); 114 doTest(fmt, randomDouble(1e100)); 115 // The use of double d such that isInfinite(100d) causes the 116 // numeric test to fail with percent formats (bug 4266589). 117 // Largest double s.t. 100d < Inf: d=1.7976931348623156E306 118 doTest(fmt, randomDouble(1e306)); 119 doTest(fmt, randomDouble(1e-323)); 120 doTest(fmt, randomDouble(1e-100)); 121 } 122 } 123 124 /** 125 * Return a random value from -range..+range. 126 */ randomDouble(double range)127 public double randomDouble(double range) { 128 double a = Math.random(); 129 return (2.0 * range * a) - range; 130 } 131 doTest(NumberFormat fmt, double value)132 public void doTest(NumberFormat fmt, double value) { 133 doTest(fmt, Double.valueOf(value)); 134 } 135 doTest(NumberFormat fmt, long value)136 public void doTest(NumberFormat fmt, long value) { 137 doTest(fmt, Long.valueOf(value)); 138 } 139 proportionalError(Number a, Number b)140 static double proportionalError(Number a, Number b) { 141 double aa = a.doubleValue(), bb = b.doubleValue(); 142 double error = aa - bb; 143 if (aa != 0 && bb != 0) error /= aa; 144 return Math.abs(error); 145 } 146 doTest(NumberFormat fmt, Number value)147 public void doTest(NumberFormat fmt, Number value) { 148 fmt.setMaximumFractionDigits(Integer.MAX_VALUE); 149 String s = fmt.format(value), s2 = null; 150 Number n = null; 151 String err = ""; 152 try { 153 if (DEBUG) logln(" " + value + " F> " + escape(s)); 154 n = fmt.parse(s); 155 if (DEBUG) logln(" " + escape(s) + " P> " + n); 156 s2 = fmt.format(n); 157 if (DEBUG) logln(" " + n + " F> " + escape(s2)); 158 159 if (STRING_COMPARE) { 160 if (!s.equals(s2)) { 161 if (fmt instanceof DecimalFormat) { 162 logln("Text mismatch: expected: " + s + ", got: " + s2 + " --- Try BigDecimal parsing."); 163 ((DecimalFormat)fmt).setParseBigDecimal(true); 164 n = fmt.parse(s); 165 if (DEBUG) logln(" " + escape(s) + " P> " + n); 166 s2 = fmt.format(n); 167 if (DEBUG) logln(" " + n + " F> " + escape(s2)); 168 ((DecimalFormat)fmt).setParseBigDecimal(false); 169 170 if (!s.equals(s2)) { 171 err = "STRING ERROR(DecimalFormat): "; 172 } 173 } else { 174 err = "STRING ERROR(NumberFormat): "; 175 } 176 } 177 } 178 179 if (EXACT_NUMERIC_COMPARE) { 180 if (value.doubleValue() != n.doubleValue()) { 181 err += "NUMERIC ERROR: "; 182 } 183 } else { 184 // Compute proportional error 185 double error = proportionalError(value, n); 186 187 if (error > MAX_ERROR) { 188 err += "NUMERIC ERROR " + error + ": "; 189 } 190 191 if (error > max_numeric_error) max_numeric_error = error; 192 if (error < min_numeric_error) min_numeric_error = error; 193 } 194 195 String message = value + typeOf(value) + " F> " + 196 escape(s) + " P> " + 197 n + typeOf(n) + " F> " + 198 escape(s2); 199 if (err.length() > 0) { 200 errln("*** " + err + " with " + 201 formatName + " in " + localeName + 202 " " + message); 203 } else { 204 logln(message); 205 } 206 } catch (ParseException e) { 207 errln("*** " + e.toString() + " with " + 208 formatName + " in " + localeName); 209 } 210 } 211 typeOf(Number n)212 static String typeOf(Number n) { 213 if (n instanceof Long) return " Long"; 214 if (n instanceof Double) return " Double"; 215 return " Number"; 216 } 217 escape(String s)218 static String escape(String s) { 219 StringBuffer buf = new StringBuffer(); 220 for (int i=0; i<s.length(); ++i) { 221 char c = s.charAt(i); 222 if (c < (char)0xFF) { 223 buf.append(c); 224 } else { 225 buf.append("\\U"); 226 buf.append(Integer.toHexString((c & 0xF000) >> 12)); 227 buf.append(Integer.toHexString((c & 0x0F00) >> 8)); 228 buf.append(Integer.toHexString((c & 0x00F0) >> 4)); 229 buf.append(Integer.toHexString(c & 0x000F)); 230 } 231 } 232 return buf.toString(); 233 } 234 } 235