/* * Copyright (c) 2016 Martin Davis. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v. 1.0 which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * * http://www.eclipse.org/org/documents/edl-v10.php. */ package org.locationtech.jts.math; import junit.framework.TestCase; import junit.textui.TestRunner; /** * Tests I/O for {@link DD}s. * * @author mbdavis * */ public class DDIOTest extends TestCase { public static void main(String args[]) { TestRunner.run(DDIOTest.class); } public DDIOTest(String name) { super(name); } public void testStandardNotation() { // standard cases checkStandardNotation(1.0, "1.0"); checkStandardNotation(0.0, "0.0"); // cases where hi is a power of 10 and lo is negative checkStandardNotation(DD.valueOf(1e12).subtract(DD.valueOf(1)), "999999999999.0"); checkStandardNotation(DD.valueOf(1e14).subtract(DD.valueOf(1)), "99999999999999.0"); checkStandardNotation(DD.valueOf(1e16).subtract(DD.valueOf(1)), "9999999999999999.0"); DD num8Dec = DD.valueOf(-379363639).divide( DD.valueOf(100000000)); checkStandardNotation(num8Dec, "-3.79363639"); checkStandardNotation(new DD(-3.79363639, 8.039137357367426E-17), "-3.7936363900000000000000000"); checkStandardNotation(DD.valueOf(34).divide( DD.valueOf(1000)), "0.034"); checkStandardNotation(1.05e3, "1050.0"); checkStandardNotation(0.34, "0.34000000000000002442490654175344"); checkStandardNotation(DD.valueOf(34).divide( DD.valueOf(100)), "0.34"); checkStandardNotation(14, "14.0"); } private void checkStandardNotation(double x, String expectedStr) { checkStandardNotation(DD.valueOf(x), expectedStr); } private void checkStandardNotation(DD x, String expectedStr) { String xStr = x.toStandardNotation(); //System.out.println("Standard Notation: " + xStr); assertEquals(expectedStr, xStr); } public void testSciNotation() { checkSciNotation(0.0, "0.0E0"); checkSciNotation(1.05e10, "1.05E10"); checkSciNotation(0.34, "3.4000000000000002442490654175344E-1"); checkSciNotation( DD.valueOf(34).divide(DD.valueOf(100)), "3.4E-1"); checkSciNotation(14, "1.4E1"); } private void checkSciNotation(double x, String expectedStr) { checkSciNotation(DD.valueOf(x), expectedStr); } private void checkSciNotation(DD x, String expectedStr) { String xStr = x.toSciNotation(); //System.out.println("Sci Notation: " + xStr); assertEquals(xStr, expectedStr); } public void testParse() { checkParse("1.05e10", 1.05E10, 1e-32); checkParse("-1.05e10", -1.05E10, 1e-32); checkParse("1.05e-10", DD.valueOf(105.).divide( DD.valueOf(100.)).divide(DD.valueOf(1.0E10)), 1e-32); checkParse("-1.05e-10", DD.valueOf(105.).divide( DD.valueOf(100.)).divide(DD.valueOf(1.0E10)) .negate(), 1e-32); /** * The Java double-precision constant 1.4 gives rise to a value which * differs from the exact binary representation down around the 17th decimal * place. Thus it will not compare exactly to the DoubleDouble * representation of the same number. To avoid this, compute the expected * value using full DD precision. */ checkParse("1.4", DD.valueOf(14).divide(DD.valueOf(10)), 1e-30); // 39.5D can be converted to an exact FP representation checkParse("39.5", 39.5, 1e-30); checkParse("-39.5", -39.5, 1e-30); } private void checkParse(String str, double expectedVal, double errBound) { checkParse(str, new DD(expectedVal), errBound); } private void checkParse(String str, DD expectedVal, double relErrBound) { DD xdd = DD.parse(str); double err = xdd.subtract(expectedVal).doubleValue(); double relErr = err / xdd.doubleValue(); //System.out.println("Parsed= " + xdd + " rel err= " + relErr); assertTrue(err <= relErrBound); } public void testParseError() { checkParseError("-1.05E2w"); checkParseError("%-1.05E2w"); checkParseError("-1.0512345678t"); } private void checkParseError(String str) { boolean foundParseError = false; try { DD.parse(str); } catch (NumberFormatException ex) { foundParseError = true; } assertTrue(foundParseError); } public void testRepeatedSqrt() { writeRepeatedSqrt(DD.valueOf(1.0)); writeRepeatedSqrt(DD.valueOf(.999999999999)); writeRepeatedSqrt(DD.PI.divide(DD.valueOf(10))); } /** * This routine simply tests for robustness of the toString function. * * @param xdd */ void writeRepeatedSqrt(DD xdd) { int count = 0; while (xdd.doubleValue() > 1e-300) { count++; double x = xdd.doubleValue(); DD xSqrt = xdd.sqrt(); String s = xSqrt.toString(); // System.out.println(count + ": " + s); DD xSqrt2 = DD.parse(s); DD xx = xSqrt2.multiply(xSqrt2); double err = Math.abs(xx.doubleValue() - x); //assertTrue(err < 1e-10); xdd = xSqrt; // square roots converge on 1 - stop when very close DD distFrom1DD = xSqrt.subtract(DD.valueOf(1.0)); double distFrom1 = distFrom1DD.doubleValue(); if (Math.abs(distFrom1) < 1.0e-40) break; } } public void testRepeatedSqr() { writeRepeatedSqr(DD.valueOf(.9)); writeRepeatedSqr(DD.PI.divide(DD.valueOf(10))); } /** * This routine simply tests for robustness of the toString function. * * @param xdd */ void writeRepeatedSqr(DD xdd) { if (xdd.ge(DD.valueOf(1))) throw new IllegalArgumentException("Argument must be < 1"); int count = 0; while (xdd.doubleValue() > 1e-300) { count++; if (count == 100) count = count; double x = xdd.doubleValue(); DD xSqr = xdd.sqr(); String s = xSqr.toString(); //System.out.println(count + ": " + s); DD xSqr2 = DD.parse(s); xdd = xSqr; } } public void testIOSquaresStress() { for (int i = 1; i < 10000; i++) { writeAndReadSqrt(i); } } /** * Tests that printing values with many decimal places works. * This tests the correctness and robustness of both output and input. * * @param x */ void writeAndReadSqrt(double x) { DD xdd = DD.valueOf(x); DD xSqrt = xdd.sqrt(); String s = xSqrt.toString(); // System.out.println(s); DD xSqrt2 = DD.parse(s); DD xx = xSqrt2.multiply(xSqrt2); String xxStr = xx.toString(); // System.out.println("==> " + xxStr); DD xx2 = DD.parse(xxStr); double err = Math.abs(xx2.doubleValue() - x); assertTrue(err < 1e-10); } }