blob: eb59a9a316b5d4a72cefee66c8ef863f6390c899 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id$ */
package org.apache.xmlgraphics.util;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.Random;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
* Test class of DoubleFormatUtil
*/
public class DoubleFormatUtilTestCase {
/**
* Test simple values as specified in the format contract.
* <p>
* Note: Some of these tests will fail if formatFast is used.
*/
@Test
public void testSimple() {
int decimals = 4;
int precision = 8;
double value = 0.0;
String expected = "0";
String actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 0.1;
expected = "0.1";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 1234.1;
expected = "1234.1";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
// rounding
value = 1234.1234567;
expected = "1234.1235";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 1234.99995;
expected = "1235";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = -1234.99995;
expected = "-1235";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 1234.99994999;
expected = "1234.9999";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
// decimals / precision switch
value = 0.00000001;
expected = "0.00000001";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = -0.00000001;
expected = "-0.00000001";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 72.00001234;
expected = "72";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
// limit precision
value = 0.000000001;
expected = "0";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 5.0e-9;
expected = "0.00000001";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 4.9999999999e-9;
expected = "0";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 2.0005e-5;
expected = "0.00002001";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 2.00049999999999e-5;
expected = "0.00002";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
// Test added after bug #43940 was reopened
value = 0.005859375;
expected = "0.00585938";
actual = format(value, 8, 8);
assertEquals(value, 8, 8, expected, actual);
value = 5.22534294505995E-4;
expected = "0.000522534294506";
actual = format(value, 17, 17);
assertEquals(value, 17, 17, expected, actual);
value = 4.9E-324;
expected = "0";
actual = format(value, 309, 309);
assertEquals(value, 309, 309, expected, actual);
value = 7.003868765287485E-280;
expected = refFormat(value, 294, 294);
actual = format(value, 294, 294);
assertEquals(value, 294, 294, expected, actual);
value = 5E-304;
expected = refFormat(value, 303, 303);
actual = format(value, 303, 303);
assertEquals(value, 303, 303, expected, actual);
value = 9.999999999999999E-250;
expected = refFormat(value, 265, 265);
actual = format(value, 265, 265);
assertEquals(value, 265, 265, expected, actual);
}
@Test
public void testLimits() {
int decimals = 19;
int precision = 19;
double value = Double.NaN;
String expected = "NaN";
String actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = Double.POSITIVE_INFINITY;
expected = "Infinity";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = Double.NEGATIVE_INFINITY;
expected = "-Infinity";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 1e-3 + Double.MIN_VALUE;
expected = "0.001";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 1e-3 - Double.MIN_VALUE;
expected = "0.001";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 1e-3;
expected = "0.001";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 0.0010000000000000002; // == Math.nextAfter(1e-3, Double.POSITIVE_INFINITY);
expected = "0.0010000000000000002";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
expected = "0.001";
actual = format(value, 18, 18);
assertEquals(value, 18, 18, expected, actual);
value = 0.0009999999999999998; // == Math.nextAfter(1e-3, Double.NEGATIVE_INFINITY);
expected = "0.0009999999999999998";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
expected = "0.001";
actual = format(value, 18, 18);
assertEquals(value, 18, 18, expected, actual);
value = 1e7 + Double.MIN_VALUE;
expected = "10000000";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 1e7 - Double.MIN_VALUE;
expected = "10000000";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 1e7;
expected = "10000000";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
value = 1.0000000000000002E7; // == Math.nextAfter(1e7, Double.POSITIVE_INFINITY);
expected = "10000000.000000002";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
expected = "10000000";
actual = format(value, 8, 8);
assertEquals(value, 8, 8, expected, actual);
value = 9999999.999999998; // == Math.nextAfter(1e7, Double.NEGATIVE_INFINITY);
expected = "9999999.999999998";
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
expected = "10000000";
actual = format(value, 8, 8);
assertEquals(value, 8, 8, expected, actual);
value = 0.000009999999999999997; // Check higher precision
expected = "0.000009999999999999997";
actual = format(value, 21, 21);
assertEquals(value, 21, 21, expected, actual);
expected = "0.00001";
actual = format(value, 20, 20);
assertEquals(value, 20, 20, expected, actual);
}
/**
* AssertEquals with a more detailed message
*/
private static void assertEquals(double value, int decimals, int precision, String expected, String actual) {
assertTrue("value: " + value + ", decimals: " + decimals + ", precision: " + precision,
expected.equals(actual));
}
/**
* The buffer used to format
*/
private StringBuffer buf = new StringBuffer();
/**
* Formats using FormatUtil#formatDouble method
*/
private String format(double value, int decimals, int precision) {
buf.setLength(0);
DoubleFormatUtil.formatDouble(value, decimals, precision, buf);
return buf.toString();
}
/**
* Formats using FormatUtil#formatDoublePrecise method
*/
private String formatPrecise(double value, int decimals, int precision) {
buf.setLength(0);
DoubleFormatUtil.formatDoublePrecise(value, decimals, precision, buf);
return buf.toString();
}
/**
* Formats using FormatUtil#formatDoubleFast method
*/
private String formatFast(double value, int decimals, int precision) {
buf.setLength(0);
DoubleFormatUtil.formatDoubleFast(value, decimals, precision, buf);
return buf.toString();
}
/**
* Formats using a BigDecimal. This is the reference (always returns the correct format)
* whereas DecimalFormat may have some formating errors regarding the last digit.
*/
private String refFormat(double value, int decimals, int precision) {
if (Double.isNaN(value) || Double.isInfinite(value)) {
return Double.toString(value);
}
buf.setLength(0);
BigDecimal bg = new BigDecimal(Double.toString(value));
int scale = Math.abs(value) < 1.0 ? precision : decimals;
bg = bg.setScale(scale, RoundingMode.HALF_UP);
//buf.append(bg.toString()); // Java 1.4
buf.append(bg.toPlainString()); // Java 1.5 and more !
if (buf.indexOf(".") >= 0) {
for (int i = buf.length() - 1; i > 1 && buf.charAt(i) == '0'; i--) {
buf.setLength(i);
}
if (buf.charAt(buf.length() - 1) == '.') {
buf.setLength(buf.length() - 1);
}
}
return buf.toString();
}
/**
* The decimal format used within formatDf method
*/
private DecimalFormat df = new DecimalFormat("0", new DecimalFormatSymbols(Locale.US));
/**
* Formats using DecimalFormat#format method
*/
private String formatDf(double value, int decimals, int precision) {
int scale = Math.abs(value) < 1.0 ? precision : decimals;
df.setMaximumFractionDigits(scale);
return df.format(value);
}
/**
* The maximum power of ten to use when testing high values double
*/
private static final int MAX_POW = 12;
/**
* Tests the formatPrecise method against the reference, with random values
*/
@Test
public void testPrecise() {
long seed = System.currentTimeMillis();
Random r = new Random();
r.setSeed(seed);
double value;
double highValue;
double lowValue;
int nbTest = 10000;
int maxDecimals = 12;
String actual;
String expected;
for (int i = nbTest; i > 0; i--) {
int decimals = r.nextInt(maxDecimals);
int precision = decimals + 3;
value = 1 + r.nextDouble(); // Use decimals and not precision
expected = refFormat(value, decimals, precision);
actual = formatPrecise(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
highValue = value * DoubleFormatUtil.tenPow(r.nextInt(MAX_POW));
expected = refFormat(highValue, decimals, precision);
actual = formatPrecise(highValue, decimals, precision);
assertEquals(highValue, decimals, precision, expected, actual);
lowValue = (value - 1) / 1000;
expected = refFormat(lowValue, decimals, precision);
actual = formatPrecise(lowValue, decimals, precision);
assertEquals(lowValue, decimals, precision, expected, actual);
}
}
/**
* Tests the format method against the reference, with random values
*/
@Test
public void testFormat() {
long seed = System.currentTimeMillis();
Random r = new Random();
r.setSeed(seed);
double value;
double highValue;
double lowValue;
int nbTest = 10000;
int maxDecimals = 12;
String actual;
String expected;
for (int i = nbTest; i > 0; i--) {
int decimals = r.nextInt(maxDecimals);
int precision = decimals + 3;
value = 1 + r.nextDouble(); // Use decimals and not precision
expected = refFormat(value, decimals, precision);
actual = format(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
highValue = value * DoubleFormatUtil.tenPow(r.nextInt(MAX_POW));
expected = refFormat(highValue, decimals, precision);
actual = format(highValue, decimals, precision);
assertEquals(highValue, decimals, precision, expected, actual);
lowValue = (value - 1) / 1000;
expected = refFormat(lowValue, decimals, precision);
actual = format(lowValue, decimals, precision);
assertEquals(lowValue, decimals, precision, expected, actual);
}
}
/**
* Tests the formatFast method against the reference, with random values.
* Disabled since the formatFast method is not accurate.
*/
@Test
@Ignore("Disabled since the formatFast method is not accurate.")
public void fast() {
long seed = System.currentTimeMillis();
Random r = new Random();
r.setSeed(seed);
double value;
double highValue;
double lowValue;
int nbTest = 10000;
int maxDecimals = 12;
String actual;
String expected;
for (int i = nbTest; i > 0; i--) {
int decimals = r.nextInt(maxDecimals);
int precision = decimals + 3;
value = 1 + r.nextDouble(); // Use decimals and not precision
expected = refFormat(value, decimals, precision);
actual = formatFast(value, decimals, precision);
assertEquals(value, decimals, precision, expected, actual);
highValue = value * DoubleFormatUtil.tenPow(r.nextInt(MAX_POW));
expected = refFormat(highValue, decimals, precision);
actual = formatFast(highValue, decimals, precision);
System.out.println(expected);
System.out.println(actual);
assertEquals(highValue, decimals, precision, expected, actual);
lowValue = (value - 1) / 1000;
expected = refFormat(lowValue, decimals, precision);
actual = formatFast(lowValue, decimals, precision);
assertEquals(lowValue, decimals, precision, expected, actual);
}
}
/**
* Performance comparison of the differents formatXXX methods,
* to see which one is the fastest in the same conditions.
*/
@Test
@Ignore("Disabled since this doesn't test correctness.")
public void performanceCompare() {
// Rename this method in testPerformanceCompare to run it within JUnit tests
// This method is quite long (depends of the value of nbTest).
long seed = System.currentTimeMillis();
Random r = new Random();
r.setSeed(seed);
double value;
double highValue;
double lowValue;
long start = System.currentTimeMillis();
int nbTest = 1000000;
int maxDecimals = 16;
r.setSeed(seed);
start = System.currentTimeMillis();
for (int i = nbTest; i > 0; i--) {
int decimals = r.nextInt(maxDecimals);
int precision = decimals + 3;
value = 1 + r.nextDouble(); // Use decimals and not precision
format(value, decimals, precision);
highValue = value * DoubleFormatUtil.tenPow(r.nextInt(MAX_POW));
format(highValue, decimals, precision);
lowValue = (value - 1) / 1000;
format(lowValue, decimals, precision);
}
long formatDuration = System.currentTimeMillis() - start;
System.out.println("Format duration: " + formatDuration + "ms to format " + (3 * nbTest) + " doubles");
r.setSeed(seed);
start = System.currentTimeMillis();
for (int i = nbTest; i > 0; i--) {
int decimals = r.nextInt(maxDecimals);
int precision = decimals + 3;
value = 1 + r.nextDouble(); // Use decimals and not precision
formatPrecise(value, decimals, precision);
highValue = value * DoubleFormatUtil.tenPow(r.nextInt(MAX_POW));
formatPrecise(highValue, decimals, precision);
lowValue = (value - 1) / 1000;
formatPrecise(lowValue, decimals, precision);
}
long preciseFormatDuration = System.currentTimeMillis() - start;
System.out.println("Format Precise duration: " + preciseFormatDuration
+ "ms to format " + (3 * nbTest) + " doubles");
r.setSeed(seed);
start = System.currentTimeMillis();
for (int i = nbTest; i > 0; i--) {
int decimals = r.nextInt(maxDecimals);
int precision = decimals + 3;
value = 1 + r.nextDouble(); // Use decimals and not precision
formatFast(value, decimals, precision);
highValue = value * DoubleFormatUtil.tenPow(r.nextInt(MAX_POW));
formatFast(highValue, decimals, precision);
lowValue = (value - 1) / 1000;
formatFast(lowValue, decimals, precision);
}
long fastFormatDuration = System.currentTimeMillis() - start;
System.out.println("Fast Format duration: " + fastFormatDuration + "ms to format " + (3 * nbTest) + " doubles");
r.setSeed(seed);
start = System.currentTimeMillis();
for (int i = nbTest; i > 0; i--) {
int decimals = r.nextInt(maxDecimals);
int precision = decimals + 3;
value = 1 + r.nextDouble(); // Use decimals and not precision
refFormat(value, decimals, precision);
highValue = value * DoubleFormatUtil.tenPow(r.nextInt(MAX_POW));
refFormat(highValue, decimals, precision);
lowValue = (value - 1) / 1000;
refFormat(lowValue, decimals, precision);
}
long bgDuration = System.currentTimeMillis() - start;
System.out.println("BigDecimal format duration: " + bgDuration + "ms to format " + (3 * nbTest) + " doubles");
r.setSeed(seed);
start = System.currentTimeMillis();
for (int i = nbTest; i > 0; i--) {
int decimals = r.nextInt(maxDecimals);
int precision = decimals + 3;
value = 1 + r.nextDouble(); // Use decimals and not precision
formatDf(value, decimals, precision);
highValue = value * DoubleFormatUtil.tenPow(r.nextInt(MAX_POW));
formatDf(highValue, decimals, precision);
lowValue = (value - 1) / 1000;
formatDf(lowValue, decimals, precision);
}
long dfDuration = System.currentTimeMillis() - start;
System.out.println("DecimalFormat duration: " + dfDuration + "ms to format " + (3 * nbTest) + " doubles");
r.setSeed(seed);
start = System.currentTimeMillis();
for (int i = nbTest; i > 0; i--) {
int decimals = r.nextInt(maxDecimals);
int precision = decimals + 3;
precision++; // Avoid warning unused local variable
value = 1 + r.nextDouble(); // Use decimals and not precision
Double.toString(value);
highValue = value * DoubleFormatUtil.tenPow(r.nextInt(MAX_POW));
Double.toString(highValue);
lowValue = (value - 1) / 1000;
Double.toString(lowValue);
}
long toStringDuration = System.currentTimeMillis() - start;
System.out.println("toString duration: " + toStringDuration + "ms to format " + (3 * nbTest) + " doubles");
}
@Test
public void testAllDoubleRanges() {
double[] values = {0, 1, 5, 4.9999, 5.0001, 9.9999, 1234567890, 0 /* The last one is random */};
Random r = new Random();
double value;
String expected;
String actual;
int minScale;
int maxScale;
for (int i = -330; i <= 315; i++) {
values[values.length - 1] = r.nextDouble();
double pow = Math.pow(10.0, i);
for (double d : values) {
value = d * pow;
minScale = 1;
maxScale = 350;
// Reduce scales (unnecessary tests)
if (i < -30) {
minScale = -i - 30;
maxScale = -i + 30;
} else if (i <= 0) {
minScale = 1;
maxScale = -i + 30;
} else {
minScale = 1;
maxScale = 30;
}
for (int scale = minScale; scale <= maxScale; scale++) {
expected = refFormat(value, scale, scale);
actual = format(value, scale, scale);
assertEquals(value, scale, scale, expected, actual);
}
}
}
}
}