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 
  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 
  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 
  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      */
 127     public double randomDouble(double range) {
 128         double a = Math.random();
 129         return (2.0 * range * a) - range;
 130     }
 131 
 132     public void doTest(NumberFormat fmt, double value) {
 133         doTest(fmt, new Double(value));
 134     }
 135 
 136     public void doTest(NumberFormat fmt, long value) {
 137         doTest(fmt, new Long(value));
 138     }
 139 
 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 
 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 
 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 
 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 }