blob: 42561b39583903767c43cdf8c257ce951a1ce7dc [file] [log] [blame]
/*
* Copyright (c) 1997, 2016, 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
* @summary round trip test NumberFormat
* @library /java/text/testlib
* @key randomness
*/
import java.text.*;
import java.util.*;
/**
* This class tests the round-trip behavior of NumberFormat, DecimalFormat, and DigitList.
* Round-trip behavior is tested by taking a numeric value and formatting it, then
* parsing the resulting string, and comparing this result with the original value.
* Two tests are applied: String preservation, and numeric preservation. String
* preservation is exact; numeric preservation is not. However, numeric preservation
* should extend to the few least-significant bits.
* //bug472
*/
public class NumberRoundTrip extends IntlTest {
static final boolean STRING_COMPARE = true;
static final boolean EXACT_NUMERIC_COMPARE = false;
static final double MAX_ERROR = 1e-14;
static boolean DEBUG = false;
static double max_numeric_error = 0;
static double min_numeric_error = 1;
String localeName, formatName;
public static void main(String[] args) throws Exception {
if (args.length > 0 && args[0].equals("-debug")) {
DEBUG = true;
String[] newargs = new String[args.length - 1];
System.arraycopy(args, 1, newargs, 0, newargs.length);
args = newargs;
}
new NumberRoundTrip().run(args);
}
public void TestNumberFormatRoundTrip() {
logln("Default Locale");
localeName = "Default Locale";
formatName = "getInstance";
doTest(NumberFormat.getInstance());
formatName = "getNumberInstance";
doTest(NumberFormat.getNumberInstance());
formatName = "getCurrencyInstance";
doTest(NumberFormat.getCurrencyInstance());
formatName = "getPercentInstance";
doTest(NumberFormat.getPercentInstance());
Locale[] loc = NumberFormat.getAvailableLocales();
for (int i=0; i<loc.length; ++i) {
logln(loc[i].getDisplayName());
localeName = loc[i].toString();
formatName = "getInstance";
doTest(NumberFormat.getInstance(loc[i]));
formatName = "getNumberInstance";
doTest(NumberFormat.getNumberInstance(loc[i]));
formatName = "getCurrencyInstance";
doTest(NumberFormat.getCurrencyInstance(loc[i]));
formatName = "getPercentInstance";
doTest(NumberFormat.getPercentInstance(loc[i]));
}
logln("Numeric error " +
min_numeric_error + " to " +
max_numeric_error);
}
public void doTest(NumberFormat fmt) {
doTest(fmt, Double.NaN);
doTest(fmt, Double.POSITIVE_INFINITY);
doTest(fmt, Double.NEGATIVE_INFINITY);
doTest(fmt, 500);
doTest(fmt, 0);
doTest(fmt, 5555555555555555L);
doTest(fmt, 55555555555555555L);
doTest(fmt, 9223372036854775807L);
doTest(fmt, 9223372036854775808.0);
doTest(fmt, -9223372036854775808L);
doTest(fmt, -9223372036854775809.0);
for (int i=0; i<2; ++i) {
doTest(fmt, randomDouble(1));
doTest(fmt, randomDouble(10000));
doTest(fmt, Math.floor(randomDouble(10000)));
doTest(fmt, randomDouble(1e50));
doTest(fmt, randomDouble(1e-50));
doTest(fmt, randomDouble(1e100));
// The use of double d such that isInfinite(100d) causes the
// numeric test to fail with percent formats (bug 4266589).
// Largest double s.t. 100d < Inf: d=1.7976931348623156E306
doTest(fmt, randomDouble(1e306));
doTest(fmt, randomDouble(1e-323));
doTest(fmt, randomDouble(1e-100));
}
}
/**
* Return a random value from -range..+range.
*/
public double randomDouble(double range) {
double a = Math.random();
return (2.0 * range * a) - range;
}
public void doTest(NumberFormat fmt, double value) {
doTest(fmt, Double.valueOf(value));
}
public void doTest(NumberFormat fmt, long value) {
doTest(fmt, Long.valueOf(value));
}
static double proportionalError(Number a, Number b) {
double aa = a.doubleValue(), bb = b.doubleValue();
double error = aa - bb;
if (aa != 0 && bb != 0) error /= aa;
return Math.abs(error);
}
public void doTest(NumberFormat fmt, Number value) {
fmt.setMaximumFractionDigits(Integer.MAX_VALUE);
String s = fmt.format(value), s2 = null;
Number n = null;
String err = "";
try {
if (DEBUG) logln(" " + value + " F> " + escape(s));
n = fmt.parse(s);
if (DEBUG) logln(" " + escape(s) + " P> " + n);
s2 = fmt.format(n);
if (DEBUG) logln(" " + n + " F> " + escape(s2));
if (STRING_COMPARE) {
if (!s.equals(s2)) {
if (fmt instanceof DecimalFormat) {
logln("Text mismatch: expected: " + s + ", got: " + s2 + " --- Try BigDecimal parsing.");
((DecimalFormat)fmt).setParseBigDecimal(true);
n = fmt.parse(s);
if (DEBUG) logln(" " + escape(s) + " P> " + n);
s2 = fmt.format(n);
if (DEBUG) logln(" " + n + " F> " + escape(s2));
((DecimalFormat)fmt).setParseBigDecimal(false);
if (!s.equals(s2)) {
err = "STRING ERROR(DecimalFormat): ";
}
} else {
err = "STRING ERROR(NumberFormat): ";
}
}
}
if (EXACT_NUMERIC_COMPARE) {
if (value.doubleValue() != n.doubleValue()) {
err += "NUMERIC ERROR: ";
}
} else {
// Compute proportional error
double error = proportionalError(value, n);
if (error > MAX_ERROR) {
err += "NUMERIC ERROR " + error + ": ";
}
if (error > max_numeric_error) max_numeric_error = error;
if (error < min_numeric_error) min_numeric_error = error;
}
String message = value + typeOf(value) + " F> " +
escape(s) + " P> " +
n + typeOf(n) + " F> " +
escape(s2);
if (err.length() > 0) {
errln("*** " + err + " with " +
formatName + " in " + localeName +
" " + message);
} else {
logln(message);
}
} catch (ParseException e) {
errln("*** " + e.toString() + " with " +
formatName + " in " + localeName);
}
}
static String typeOf(Number n) {
if (n instanceof Long) return " Long";
if (n instanceof Double) return " Double";
return " Number";
}
static String escape(String s) {
StringBuffer buf = new StringBuffer();
for (int i=0; i<s.length(); ++i) {
char c = s.charAt(i);
if (c < (char)0xFF) {
buf.append(c);
} else {
buf.append("\\U");
buf.append(Integer.toHexString((c & 0xF000) >> 12));
buf.append(Integer.toHexString((c & 0x0F00) >> 8));
buf.append(Integer.toHexString((c & 0x00F0) >> 4));
buf.append(Integer.toHexString(c & 0x000F));
}
}
return buf.toString();
}
}