| /* |
| * 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. |
| */ |
| package org.apache.commons.math4.legacy.core.jdkmath; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.math.RoundingMode; |
| |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.jupiter.api.Assertions; |
| import org.apache.commons.numbers.core.ArithmeticUtils; |
| import org.apache.commons.numbers.core.Precision; |
| import org.apache.commons.math4.legacy.core.dfp.Dfp; |
| import org.apache.commons.math4.legacy.core.dfp.DfpField; |
| import org.apache.commons.math4.legacy.core.dfp.DfpMath; |
| import org.apache.commons.rng.UniformRandomProvider; |
| import org.apache.commons.rng.simple.RandomSource; |
| |
| // Unit test should be moved to module "commons-math-core". |
| // [Currently, it can't be because it depends on "legacy" classes.] |
| import org.apache.commons.math4.core.jdkmath.AccurateMath; |
| |
| public class AccurateMathTest { |
| // CHECKSTYLE: stop Regexp |
| // The above comment allows System.out.print |
| |
| private static final double MAX_ERROR_ULP = 0.51; |
| private static final int NUMBER_OF_TRIALS = 1000; |
| |
| private DfpField field; |
| private UniformRandomProvider generator; |
| |
| @Before |
| public void setUp() { |
| field = new DfpField(40); |
| generator = RandomSource.MT.create(6176597458463500194L); |
| } |
| |
| @Test |
| public void testMinMaxDouble() { |
| double[][] pairs = { |
| {-50.0, 50.0}, |
| {Double.POSITIVE_INFINITY, 1.0}, |
| {Double.NEGATIVE_INFINITY, 1.0}, |
| {Double.NaN, 1.0}, |
| {Double.POSITIVE_INFINITY, 0.0}, |
| {Double.NEGATIVE_INFINITY, 0.0}, |
| {Double.NaN, 0.0}, |
| {Double.NaN, Double.NEGATIVE_INFINITY}, |
| {Double.NaN, Double.POSITIVE_INFINITY}, |
| {Precision.SAFE_MIN, Precision.EPSILON} |
| }; |
| for (double[] pair : pairs) { |
| assertEquals("min(" + pair[0] + ", " + pair[1] + ")", |
| Math.min(pair[0], pair[1]), |
| AccurateMath.min(pair[0], pair[1]), |
| Precision.EPSILON); |
| assertEquals("min(" + pair[1] + ", " + pair[0] + ")", |
| Math.min(pair[1], pair[0]), |
| AccurateMath.min(pair[1], pair[0]), |
| Precision.EPSILON); |
| assertEquals("max(" + pair[0] + ", " + pair[1] + ")", |
| Math.max(pair[0], pair[1]), |
| AccurateMath.max(pair[0], pair[1]), |
| Precision.EPSILON); |
| assertEquals("max(" + pair[1] + ", " + pair[0] + ")", |
| Math.max(pair[1], pair[0]), |
| AccurateMath.max(pair[1], pair[0]), |
| Precision.EPSILON); |
| } |
| } |
| |
| @Test |
| public void testMinMaxFloat() { |
| float[][] pairs = { |
| {-50.0f, 50.0f}, |
| {Float.POSITIVE_INFINITY, 1.0f}, |
| {Float.NEGATIVE_INFINITY, 1.0f}, |
| {Float.NaN, 1.0f}, |
| {Float.POSITIVE_INFINITY, 0.0f}, |
| {Float.NEGATIVE_INFINITY, 0.0f}, |
| {Float.NaN, 0.0f}, |
| {Float.NaN, Float.NEGATIVE_INFINITY}, |
| {Float.NaN, Float.POSITIVE_INFINITY} |
| }; |
| for (float[] pair : pairs) { |
| assertEquals("min(" + pair[0] + ", " + pair[1] + ")", |
| Math.min(pair[0], pair[1]), |
| AccurateMath.min(pair[0], pair[1]), |
| Precision.EPSILON); |
| assertEquals("min(" + pair[1] + ", " + pair[0] + ")", |
| Math.min(pair[1], pair[0]), |
| AccurateMath.min(pair[1], pair[0]), |
| Precision.EPSILON); |
| assertEquals("max(" + pair[0] + ", " + pair[1] + ")", |
| Math.max(pair[0], pair[1]), |
| AccurateMath.max(pair[0], pair[1]), |
| Precision.EPSILON); |
| assertEquals("max(" + pair[1] + ", " + pair[0] + ")", |
| Math.max(pair[1], pair[0]), |
| AccurateMath.max(pair[1], pair[0]), |
| Precision.EPSILON); |
| } |
| } |
| |
| @Test |
| public void testConstants() { |
| assertEquals(Math.PI, AccurateMath.PI, 1.0e-20); |
| assertEquals(Math.E, AccurateMath.E, 1.0e-20); |
| } |
| |
| @Test |
| public void testAtan2() { |
| double y1 = 1.2713504628280707e10; |
| double x1 = -5.674940885228782e-10; |
| assertEquals(Math.atan2(y1, x1), AccurateMath.atan2(y1, x1), 2 * Precision.EPSILON); |
| double y2 = 0.0; |
| double x2 = Double.POSITIVE_INFINITY; |
| assertEquals(Math.atan2(y2, x2), AccurateMath.atan2(y2, x2), Precision.SAFE_MIN); |
| } |
| |
| @Test |
| public void testHyperbolic() { |
| double maxErr = 0; |
| for (double x = -30; x < 30; x += 0.001) { |
| double tst = AccurateMath.sinh(x); |
| double ref = Math.sinh(x); |
| maxErr = AccurateMath.max(maxErr, AccurateMath.abs(ref - tst) / AccurateMath.ulp(ref)); |
| } |
| assertEquals(0, maxErr, 2); |
| |
| maxErr = 0; |
| for (double x = -30; x < 30; x += 0.001) { |
| double tst = AccurateMath.cosh(x); |
| double ref = Math.cosh(x); |
| maxErr = AccurateMath.max(maxErr, AccurateMath.abs(ref - tst) / AccurateMath.ulp(ref)); |
| } |
| assertEquals(0, maxErr, 2); |
| |
| maxErr = 0; |
| for (double x = -0.5; x < 0.5; x += 0.001) { |
| double tst = AccurateMath.tanh(x); |
| double ref = Math.tanh(x); |
| maxErr = AccurateMath.max(maxErr, AccurateMath.abs(ref - tst) / AccurateMath.ulp(ref)); |
| } |
| assertEquals(0, maxErr, 4); |
| |
| } |
| |
| @Test |
| public void testMath904() { |
| final double x = -1; |
| final double y = (5 + 1e-15) * 1e15; |
| assertEquals(Math.pow(x, y), |
| AccurateMath.pow(x, y), 0); |
| assertEquals(Math.pow(x, -y), |
| AccurateMath.pow(x, -y), 0); |
| } |
| |
| @Test |
| public void testMath905LargePositive() { |
| final double start = StrictMath.log(Double.MAX_VALUE); |
| final double endT = StrictMath.sqrt(2) * StrictMath.sqrt(Double.MAX_VALUE); |
| final double end = 2 * StrictMath.log(endT); |
| |
| double maxErr = 0; |
| for (double x = start; x < end; x += 1e-3) { |
| final double tst = AccurateMath.cosh(x); |
| final double ref = Math.cosh(x); |
| maxErr = AccurateMath.max(maxErr, AccurateMath.abs(ref - tst) / AccurateMath.ulp(ref)); |
| } |
| assertEquals(0, maxErr, 3); |
| |
| for (double x = start; x < end; x += 1e-3) { |
| final double tst = AccurateMath.sinh(x); |
| final double ref = Math.sinh(x); |
| maxErr = AccurateMath.max(maxErr, AccurateMath.abs(ref - tst) / AccurateMath.ulp(ref)); |
| } |
| assertEquals(0, maxErr, 3); |
| } |
| |
| @Test |
| public void testMath905LargeNegative() { |
| final double start = -StrictMath.log(Double.MAX_VALUE); |
| final double endT = StrictMath.sqrt(2) * StrictMath.sqrt(Double.MAX_VALUE); |
| final double end = -2 * StrictMath.log(endT); |
| |
| double maxErr = 0; |
| for (double x = start; x > end; x -= 1e-3) { |
| final double tst = AccurateMath.cosh(x); |
| final double ref = Math.cosh(x); |
| maxErr = AccurateMath.max(maxErr, AccurateMath.abs(ref - tst) / AccurateMath.ulp(ref)); |
| } |
| assertEquals(0, maxErr, 3); |
| |
| for (double x = start; x > end; x -= 1e-3) { |
| final double tst = AccurateMath.sinh(x); |
| final double ref = Math.sinh(x); |
| maxErr = AccurateMath.max(maxErr, AccurateMath.abs(ref - tst) / AccurateMath.ulp(ref)); |
| } |
| assertEquals(0, maxErr, 3); |
| } |
| |
| @Test |
| public void testMath1269() { |
| final double arg = 709.8125; |
| final double vM = Math.exp(arg); |
| final double vFM = AccurateMath.exp(arg); |
| assertTrue("exp(" + arg + ") is " + vFM + " instead of " + vM, |
| Precision.equalsIncludingNaN(vM, vFM)); |
| } |
| |
| @Test |
| public void testHyperbolicInverses() { |
| double maxErr = 0; |
| for (double x = -30; x < 30; x += 0.01) { |
| maxErr = AccurateMath.max(maxErr, AccurateMath.abs(x - AccurateMath.sinh(AccurateMath.asinh(x))) / (2 * AccurateMath.ulp(x))); |
| } |
| assertEquals(0, maxErr, 3); |
| |
| maxErr = 0; |
| for (double x = 1; x < 30; x += 0.01) { |
| maxErr = AccurateMath.max(maxErr, AccurateMath.abs(x - AccurateMath.cosh(AccurateMath.acosh(x))) / (2 * AccurateMath.ulp(x))); |
| } |
| assertEquals(0, maxErr, 2); |
| |
| maxErr = 0; |
| for (double x = -1 + Precision.EPSILON; x < 1 - Precision.EPSILON; x += 0.0001) { |
| maxErr = AccurateMath.max(maxErr, AccurateMath.abs(x - AccurateMath.tanh(AccurateMath.atanh(x))) / (2 * AccurateMath.ulp(x))); |
| } |
| assertEquals(0, maxErr, 2); |
| } |
| |
| @Test |
| public void testLogAccuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < NUMBER_OF_TRIALS; i++) { |
| double x = Math.exp(generator.nextDouble() * 1416.0 - 708.0) * generator.nextDouble(); |
| // double x = generator.nextDouble()*2.0; |
| double tst = AccurateMath.log(x); |
| double ref = DfpMath.log(field.newDfp(x)).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0.0) { |
| double ulp = Math.abs(ref - |
| Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(DfpMath.log(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble(); |
| // System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("log() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testLog10Accuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < NUMBER_OF_TRIALS; i++) { |
| double x = Math.exp(generator.nextDouble() * 1416.0 - 708.0) * generator.nextDouble(); |
| // double x = generator.nextDouble()*2.0; |
| double tst = AccurateMath.log10(x); |
| double ref = DfpMath.log(field.newDfp(x)).divide(DfpMath.log(field.newDfp("10"))).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0.0) { |
| double ulp = Math.abs(ref - |
| Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(DfpMath.log(field.newDfp(x)).divide(DfpMath.log(field.newDfp("10")))).divide(field.newDfp(ulp)).toDouble(); |
| // System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("log10() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testLog1pAccuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < NUMBER_OF_TRIALS; i++) { |
| double x = Math.exp(generator.nextDouble() * 10.0 - 5.0) * generator.nextDouble(); |
| // double x = generator.nextDouble()*2.0; |
| double tst = AccurateMath.log1p(x); |
| double ref = DfpMath.log(field.newDfp(x).add(field.getOne())).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0.0) { |
| double ulp = Math.abs(ref - |
| Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(DfpMath.log(field.newDfp(x).add(field.getOne()))).divide(field.newDfp(ulp)).toDouble(); |
| // System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("log1p() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testLog1pSpecialCases() { |
| assertTrue("Logp of -1.0 should be -Inf", Double.isInfinite(AccurateMath.log1p(-1.0))); |
| } |
| |
| @Test |
| public void testLogSpecialCases() { |
| assertEquals("Log of zero should be -Inf", Double.NEGATIVE_INFINITY, AccurateMath.log(0.0), 1.0); |
| assertEquals("Log of -zero should be -Inf", Double.NEGATIVE_INFINITY, AccurateMath.log(-0.0), 1.0); |
| assertTrue("Log of NaN should be NaN", Double.isNaN(AccurateMath.log(Double.NaN))); |
| assertTrue("Log of negative number should be NaN", Double.isNaN(AccurateMath.log(-1.0))); |
| assertEquals("Log of Double.MIN_VALUE should be -744.4400719213812", -744.4400719213812, AccurateMath.log(Double.MIN_VALUE), Precision.EPSILON); |
| assertEquals("Log of infinity should be infinity", Double.POSITIVE_INFINITY, AccurateMath.log(Double.POSITIVE_INFINITY), 1.0); |
| } |
| |
| @Test |
| public void testExpSpecialCases() { |
| // Smallest value that will round up to Double.MIN_VALUE |
| assertEquals(Double.MIN_VALUE, AccurateMath.exp(-745.1332191019411), Precision.EPSILON); |
| assertEquals("exp(-745.1332191019412) should be 0.0", 0.0, AccurateMath.exp(-745.1332191019412), Precision.EPSILON); |
| assertTrue("exp of NaN should be NaN", Double.isNaN(AccurateMath.exp(Double.NaN))); |
| assertEquals("exp of infinity should be infinity", Double.POSITIVE_INFINITY, AccurateMath.exp(Double.POSITIVE_INFINITY), 1.0); |
| assertEquals("exp of -infinity should be 0.0", 0.0, AccurateMath.exp(Double.NEGATIVE_INFINITY), Precision.EPSILON); |
| assertEquals("exp(1) should be Math.E", Math.E, AccurateMath.exp(1.0), Precision.EPSILON); |
| } |
| |
| @Test |
| public void testPowSpecialCases() { |
| final double EXACT = -1.0; |
| |
| assertEquals("pow(-1, 0) should be 1.0", 1.0, AccurateMath.pow(-1.0, 0.0), Precision.EPSILON); |
| assertEquals("pow(-1, -0) should be 1.0", 1.0, AccurateMath.pow(-1.0, -0.0), Precision.EPSILON); |
| assertEquals("pow(PI, 1.0) should be PI", AccurateMath.PI, AccurateMath.pow(AccurateMath.PI, 1.0), Precision.EPSILON); |
| assertEquals("pow(-PI, 1.0) should be -PI", -AccurateMath.PI, AccurateMath.pow(-AccurateMath.PI, 1.0), Precision.EPSILON); |
| assertTrue("pow(PI, NaN) should be NaN", Double.isNaN(AccurateMath.pow(Math.PI, Double.NaN))); |
| assertTrue("pow(NaN, PI) should be NaN", Double.isNaN(AccurateMath.pow(Double.NaN, Math.PI))); |
| assertEquals("pow(2.0, Infinity) should be Infinity", Double.POSITIVE_INFINITY, AccurateMath.pow(2.0, Double.POSITIVE_INFINITY), 1.0); |
| assertEquals("pow(0.5, -Infinity) should be Infinity", Double.POSITIVE_INFINITY, AccurateMath.pow(0.5, Double.NEGATIVE_INFINITY), 1.0); |
| assertEquals("pow(0.5, Infinity) should be 0.0", 0.0, AccurateMath.pow(0.5, Double.POSITIVE_INFINITY), Precision.EPSILON); |
| assertEquals("pow(2.0, -Infinity) should be 0.0", 0.0, AccurateMath.pow(2.0, Double.NEGATIVE_INFINITY), Precision.EPSILON); |
| assertEquals("pow(0.0, 0.5) should be 0.0", 0.0, AccurateMath.pow(0.0, 0.5), Precision.EPSILON); |
| assertEquals("pow(Infinity, -0.5) should be 0.0", 0.0, AccurateMath.pow(Double.POSITIVE_INFINITY, -0.5), Precision.EPSILON); |
| assertEquals("pow(0.0, -0.5) should be Inf", Double.POSITIVE_INFINITY, AccurateMath.pow(0.0, -0.5), 1.0); |
| assertEquals("pow(Inf, 0.5) should be Inf", Double.POSITIVE_INFINITY, AccurateMath.pow(Double.POSITIVE_INFINITY, 0.5), 1.0); |
| assertEquals("pow(-0.0, -3.0) should be -Inf", Double.NEGATIVE_INFINITY, AccurateMath.pow(-0.0, -3.0), 1.0); |
| assertEquals("pow(-0.0, Infinity) should be 0.0", 0.0, AccurateMath.pow(-0.0, Double.POSITIVE_INFINITY), Precision.EPSILON); |
| assertTrue("pow(-0.0, NaN) should be NaN", Double.isNaN(AccurateMath.pow(-0.0, Double.NaN))); |
| assertEquals("pow(-0.0, -tiny) should be Infinity", Double.POSITIVE_INFINITY, AccurateMath.pow(-0.0, -Double.MIN_VALUE), 1.0); |
| assertEquals("pow(-0.0, -huge) should be Infinity", Double.POSITIVE_INFINITY, AccurateMath.pow(-0.0, -Double.MAX_VALUE), 1.0); |
| assertEquals("pow(-Inf, 3.0) should be -Inf", Double.NEGATIVE_INFINITY, AccurateMath.pow(Double.NEGATIVE_INFINITY, 3.0), 1.0); |
| assertEquals("pow(-Inf, -3.0) should be -0.0", -0.0, AccurateMath.pow(Double.NEGATIVE_INFINITY, -3.0), EXACT); |
| assertEquals("pow(-0.0, -3.5) should be Inf", Double.POSITIVE_INFINITY, AccurateMath.pow(-0.0, -3.5), 1.0); |
| assertEquals("pow(Inf, 3.5) should be Inf", Double.POSITIVE_INFINITY, AccurateMath.pow(Double.POSITIVE_INFINITY, 3.5), 1.0); |
| assertEquals("pow(-2.0, 3.0) should be -8.0", -8.0, AccurateMath.pow(-2.0, 3.0), Precision.EPSILON); |
| assertTrue("pow(-2.0, 3.5) should be NaN", Double.isNaN(AccurateMath.pow(-2.0, 3.5))); |
| assertTrue("pow(NaN, -Infinity) should be NaN", Double.isNaN(AccurateMath.pow(Double.NaN, Double.NEGATIVE_INFINITY))); |
| assertEquals("pow(NaN, 0.0) should be 1.0", 1.0, AccurateMath.pow(Double.NaN, 0.0), Precision.EPSILON); |
| assertEquals("pow(-Infinity, -Infinity) should be 0.0", 0.0, AccurateMath.pow(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY), Precision.EPSILON); |
| assertEquals("pow(-huge, -huge) should be 0.0", 0.0, AccurateMath.pow(-Double.MAX_VALUE, -Double.MAX_VALUE), Precision.EPSILON); |
| assertTrue("pow(-huge, huge) should be +Inf", Double.isInfinite(AccurateMath.pow(-Double.MAX_VALUE, Double.MAX_VALUE))); |
| assertTrue("pow(NaN, -Infinity) should be NaN", Double.isNaN(AccurateMath.pow(Double.NaN, Double.NEGATIVE_INFINITY))); |
| assertEquals("pow(NaN, -0.0) should be 1.0", 1.0, AccurateMath.pow(Double.NaN, -0.0), Precision.EPSILON); |
| assertEquals("pow(-Infinity, -Infinity) should be 0.0", 0.0, AccurateMath.pow(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY), Precision.EPSILON); |
| assertEquals("pow(-huge, -huge) should be 0.0", 0.0, AccurateMath.pow(-Double.MAX_VALUE, -Double.MAX_VALUE), Precision.EPSILON); |
| assertEquals("pow(-huge, huge) should be +Inf", Double.POSITIVE_INFINITY, AccurateMath.pow(-Double.MAX_VALUE, Double.MAX_VALUE), 1.0); |
| |
| // Added tests for a 100% coverage |
| |
| assertTrue("pow(+Inf, NaN) should be NaN", Double.isNaN(AccurateMath.pow(Double.POSITIVE_INFINITY, Double.NaN))); |
| assertTrue("pow(1.0, +Inf) should be NaN", Double.isNaN(AccurateMath.pow(1.0, Double.POSITIVE_INFINITY))); |
| assertTrue("pow(-Inf, NaN) should be NaN", Double.isNaN(AccurateMath.pow(Double.NEGATIVE_INFINITY, Double.NaN))); |
| assertEquals("pow(-Inf, -1.0) should be -0.0", -0.0, AccurateMath.pow(Double.NEGATIVE_INFINITY, -1.0), EXACT); |
| assertEquals("pow(-Inf, -2.0) should be 0.0", 0.0, AccurateMath.pow(Double.NEGATIVE_INFINITY, -2.0), EXACT); |
| assertEquals("pow(-Inf, 1.0) should be -Inf", Double.NEGATIVE_INFINITY, AccurateMath.pow(Double.NEGATIVE_INFINITY, 1.0), 1.0); |
| assertEquals("pow(-Inf, 2.0) should be +Inf", Double.POSITIVE_INFINITY, AccurateMath.pow(Double.NEGATIVE_INFINITY, 2.0), 1.0); |
| assertTrue("pow(1.0, -Inf) should be NaN", Double.isNaN(AccurateMath.pow(1.0, Double.NEGATIVE_INFINITY))); |
| assertEquals("pow(-0.0, 1.0) should be -0.0", -0.0, AccurateMath.pow(-0.0, 1.0), EXACT); |
| assertEquals("pow(0.0, 1.0) should be 0.0", 0.0, AccurateMath.pow(0.0, 1.0), EXACT); |
| assertEquals("pow(0.0, +Inf) should be 0.0", 0.0, AccurateMath.pow(0.0, Double.POSITIVE_INFINITY), EXACT); |
| assertEquals("pow(-0.0, even) should be 0.0", 0.0, AccurateMath.pow(-0.0, 6.0), EXACT); |
| assertEquals("pow(-0.0, odd) should be -0.0", -0.0, AccurateMath.pow(-0.0, 13.0), EXACT); |
| assertEquals("pow(-0.0, -even) should be +Inf", Double.POSITIVE_INFINITY, AccurateMath.pow(-0.0, -6.0), EXACT); |
| assertEquals("pow(-0.0, -odd) should be -Inf", Double.NEGATIVE_INFINITY, AccurateMath.pow(-0.0, -13.0), EXACT); |
| assertEquals("pow(-2.0, 4.0) should be 16.0", 16.0, AccurateMath.pow(-2.0, 4.0), EXACT); |
| assertEquals("pow(-2.0, 4.5) should be NaN", Double.NaN, AccurateMath.pow(-2.0, 4.5), EXACT); |
| assertEquals("pow(-0.0, -0.0) should be 1.0", 1.0, AccurateMath.pow(-0.0, -0.0), EXACT); |
| assertEquals("pow(-0.0, 0.0) should be 1.0", 1.0, AccurateMath.pow(-0.0, 0.0), EXACT); |
| assertEquals("pow(0.0, -0.0) should be 1.0", 1.0, AccurateMath.pow(0.0, -0.0), EXACT); |
| assertEquals("pow(0.0, 0.0) should be 1.0", 1.0, AccurateMath.pow(0.0, 0.0), EXACT); |
| } |
| |
| @Test(timeout = 20000L) |
| public void testPowAllSpecialCases() { |
| final double EXACT = -1.0; |
| final double[] DOUBLES = new double[] { |
| Double.NEGATIVE_INFINITY, -0.0, Double.NaN, 0.0, Double.POSITIVE_INFINITY, |
| Long.MIN_VALUE, Integer.MIN_VALUE, Short.MIN_VALUE, Byte.MIN_VALUE, |
| -(double)Long.MIN_VALUE, -(double)Integer.MIN_VALUE, -(double)Short.MIN_VALUE, -(double)Byte.MIN_VALUE, |
| Byte.MAX_VALUE, Short.MAX_VALUE, Integer.MAX_VALUE, Long.MAX_VALUE, |
| -Byte.MAX_VALUE, -Short.MAX_VALUE, -Integer.MAX_VALUE, -Long.MAX_VALUE, |
| Float.MAX_VALUE, Double.MAX_VALUE, Double.MIN_VALUE, Float.MIN_VALUE, |
| -Float.MAX_VALUE, -Double.MAX_VALUE, -Double.MIN_VALUE, -Float.MIN_VALUE, |
| 0.5, 0.1, 0.2, 0.8, 1.1, 1.2, 1.5, 1.8, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 1.3, 2.2, 2.5, 2.8, 33.0, 33.1, 33.5, 33.8, 10.0, 300.0, 400.0, 500.0, |
| -0.5, -0.1, -0.2, -0.8, -1.1, -1.2, -1.5, -1.8, -1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0, -9.0, -1.3, -2.2, -2.5, -2.8, -33.0, -33.1, -33.5, -33.8, -10.0, -300.0, -400.0, -500.0 |
| }; |
| |
| // Special cases from Math.pow javadoc: |
| // If the second argument is positive or negative zero, then the result is 1.0. |
| for (double d : DOUBLES) { |
| assertEquals(1.0, AccurateMath.pow(d, 0.0), EXACT); |
| } |
| for (double d : DOUBLES) { |
| assertEquals(1.0, AccurateMath.pow(d, -0.0), EXACT); |
| } |
| // If the second argument is 1.0, then the result is the same as the first argument. |
| for (double d : DOUBLES) { |
| assertEquals(d, AccurateMath.pow(d, 1.0), EXACT); |
| } |
| // If the second argument is NaN, then the result is NaN. |
| for (double d : DOUBLES) { |
| assertEquals(Double.NaN, AccurateMath.pow(d, Double.NaN), EXACT); |
| } |
| // If the first argument is NaN and the second argument is nonzero, then the result is NaN. |
| for (double i : DOUBLES) { |
| if (i != 0.0) { |
| assertEquals(Double.NaN, AccurateMath.pow(Double.NaN, i), EXACT); |
| } |
| } |
| // If the absolute value of the first argument is greater than 1 and the second argument is positive infinity, or |
| // the absolute value of the first argument is less than 1 and the second argument is negative infinity, then the result is positive infinity. |
| for (double d : DOUBLES) { |
| if (Math.abs(d) > 1.0) { |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.pow(d, Double.POSITIVE_INFINITY), EXACT); |
| } |
| } |
| for (double d : DOUBLES) { |
| if (Math.abs(d) < 1.0) { |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.pow(d, Double.NEGATIVE_INFINITY), EXACT); |
| } |
| } |
| // If the absolute value of the first argument is greater than 1 and the second argument is negative infinity, or |
| // the absolute value of the first argument is less than 1 and the second argument is positive infinity, then the result is positive zero. |
| for (double d : DOUBLES) { |
| if (Math.abs(d) > 1.0) { |
| assertEquals(0.0, AccurateMath.pow(d, Double.NEGATIVE_INFINITY), EXACT); |
| } |
| } |
| for (double d : DOUBLES) { |
| if (Math.abs(d) < 1.0) { |
| assertEquals(0.0, AccurateMath.pow(d, Double.POSITIVE_INFINITY), EXACT); |
| } |
| } |
| // If the absolute value of the first argument equals 1 and the second argument is infinite, then the result is NaN. |
| assertEquals(Double.NaN, AccurateMath.pow(1.0, Double.POSITIVE_INFINITY), EXACT); |
| assertEquals(Double.NaN, AccurateMath.pow(1.0, Double.NEGATIVE_INFINITY), EXACT); |
| assertEquals(Double.NaN, AccurateMath.pow(-1.0, Double.POSITIVE_INFINITY), EXACT); |
| assertEquals(Double.NaN, AccurateMath.pow(-1.0, Double.NEGATIVE_INFINITY), EXACT); |
| // If the first argument is positive zero and the second argument is greater than zero, or |
| // the first argument is positive infinity and the second argument is less than zero, then the result is positive zero. |
| for (double i : DOUBLES) { |
| if (i > 0.0) { |
| assertEquals(0.0, AccurateMath.pow(0.0, i), EXACT); |
| } |
| } |
| for (double i : DOUBLES) { |
| if (i < 0.0) { |
| assertEquals(0.0, AccurateMath.pow(Double.POSITIVE_INFINITY, i), EXACT); |
| } |
| } |
| // If the first argument is positive zero and the second argument is less than zero, or |
| // the first argument is positive infinity and the second argument is greater than zero, then the result is positive infinity. |
| for (double i : DOUBLES) { |
| if (i < 0.0) { |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.pow(0.0, i), EXACT); |
| } |
| } |
| for (double i : DOUBLES) { |
| if (i > 0.0) { |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.pow(Double.POSITIVE_INFINITY, i), EXACT); |
| } |
| } |
| // If the first argument is negative zero and the second argument is greater than zero but not a finite odd integer, or |
| // the first argument is negative infinity and the second argument is less than zero but not a finite odd integer, then the result is positive zero. |
| for (double i : DOUBLES) { |
| if (i > 0.0 && (Double.isInfinite(i) || i % 2.0 == 0.0)) { |
| assertEquals(0.0, AccurateMath.pow(-0.0, i), EXACT); |
| } |
| } |
| for (double i : DOUBLES) { |
| if (i < 0.0 && (Double.isInfinite(i) || i % 2.0 == 0.0)) { |
| assertEquals(0.0, AccurateMath.pow(Double.NEGATIVE_INFINITY, i), EXACT); |
| } |
| } |
| // If the first argument is negative zero and the second argument is a positive finite odd integer, or |
| // the first argument is negative infinity and the second argument is a negative finite odd integer, then the result is negative zero. |
| for (double i : DOUBLES) { |
| if (i > 0.0 && i % 2.0 == 1.0) { |
| assertEquals(-0.0, AccurateMath.pow(-0.0, i), EXACT); |
| } |
| } |
| for (double i : DOUBLES) { |
| if (i < 0.0 && i % 2.0 == -1.0) { |
| assertEquals(-0.0, AccurateMath.pow(Double.NEGATIVE_INFINITY, i), EXACT); |
| } |
| } |
| // If the first argument is negative zero and the second argument is less than zero but not a finite odd integer, or |
| // the first argument is negative infinity and the second argument is greater than zero but not a finite odd integer, then the result is positive infinity. |
| for (double i : DOUBLES) { |
| if (i > 0.0 && (Double.isInfinite(i) || i % 2.0 == 0.0)) { |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.pow(Double.NEGATIVE_INFINITY, i), EXACT); |
| } |
| } |
| for (double i : DOUBLES) { |
| if (i < 0.0 && (Double.isInfinite(i) || i % 2.0 == 0.0)) { |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.pow(-0.0, i), EXACT); |
| } |
| } |
| // If the first argument is negative zero and the second argument is a negative finite odd integer, or |
| // the first argument is negative infinity and the second argument is a positive finite odd integer, then the result is negative infinity. |
| for (double i : DOUBLES) { |
| if (i > 0.0 && i % 2.0 == 1.0) { |
| assertEquals(Double.NEGATIVE_INFINITY, AccurateMath.pow(Double.NEGATIVE_INFINITY, i), EXACT); |
| } |
| } |
| for (double i : DOUBLES) { |
| if (i < 0.0 && i % 2.0 == -1.0) { |
| assertEquals(Double.NEGATIVE_INFINITY, AccurateMath.pow(-0.0, i), EXACT); |
| } |
| } |
| for (double d : DOUBLES) { |
| // If the first argument is finite and less than zero |
| if (d < 0.0 && Math.abs(d) <= Double.MAX_VALUE) { |
| for (double i : DOUBLES) { |
| if (Math.abs(i) <= Double.MAX_VALUE) { |
| // if the second argument is a finite even integer, the result is equal to the result of raising the absolute value of the first argument to the power of the second argument |
| if (i % 2.0 == 0.0) { |
| assertEquals(AccurateMath.pow(-d, i), AccurateMath.pow(d, i), EXACT); |
| } else if (Math.abs(i) % 2.0 == 1.0) { |
| // if the second argument is a finite odd integer, the result is equal to the negative of the result of raising the absolute value of the first argument to the power of the second argument |
| assertEquals(-AccurateMath.pow(-d, i), AccurateMath.pow(d, i), EXACT); |
| } else { |
| // if the second argument is finite and not an integer, then the result is NaN. |
| assertEquals(Double.NaN, AccurateMath.pow(d, i), EXACT); |
| } |
| } |
| } |
| } |
| } |
| // If both arguments are integers, then the result is exactly equal to the mathematical result of raising the first argument to the power |
| // of the second argument if that result can in fact be represented exactly as a double value. |
| final int TOO_BIG_TO_CALCULATE = 18; // This value is empirical: 2^18 > 200.000 resulting bits after raising d to power i. |
| for (double d : DOUBLES) { |
| if (d % 1.0 == 0.0) { |
| boolean dNegative = Double.doubleToRawLongBits(d) < 0L; |
| for (double i : DOUBLES) { |
| if (i % 1.0 == 0.0) { |
| BigInteger bd = BigDecimal.valueOf(d).toBigInteger().abs(); |
| BigInteger bi = BigDecimal.valueOf(i).toBigInteger().abs(); |
| double expected; |
| if (bd.bitLength() > 1 && bi.bitLength() > 1 && 32 - Integer.numberOfLeadingZeros(bd.bitLength()) + bi.bitLength() > TOO_BIG_TO_CALCULATE) { |
| // Result would be too big. |
| expected = i < 0.0 ? 0.0 : Double.POSITIVE_INFINITY; |
| } else { |
| BigInteger res = ArithmeticUtils.pow(bd, bi); |
| if (i >= 0.0) { |
| expected = res.doubleValue(); |
| } else if (res.signum() == 0) { |
| expected = Double.POSITIVE_INFINITY; |
| } else { |
| expected = BigDecimal.ONE.divide(new BigDecimal(res), 1024, RoundingMode.HALF_UP).doubleValue(); |
| } |
| } |
| if (dNegative && bi.testBit(0)) { |
| expected = -expected; |
| } |
| assertEquals(d + "^" + i + "=" + expected + ", Math.pow=" + Math.pow(d, i), expected, AccurateMath.pow(d, i), expected == 0.0 || Double.isInfinite(expected) || Double.isNaN(expected) ? EXACT : 2.0 * Math.ulp(expected)); |
| } |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testPowLargeIntegralDouble() { |
| double y = AccurateMath.scalb(1.0, 65); |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.pow(AccurateMath.nextUp(1.0), y), 1.0); |
| assertEquals(1.0, AccurateMath.pow(1.0, y), 1.0); |
| assertEquals(0.0, AccurateMath.pow(AccurateMath.nextDown(1.0), y), 1.0); |
| assertEquals(0.0, AccurateMath.pow(AccurateMath.nextUp(-1.0), y), 1.0); |
| assertEquals(1.0, AccurateMath.pow(-1.0, y), 1.0); |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.pow(AccurateMath.nextDown(-1.0), y), 1.0); |
| } |
| |
| @Test |
| public void testAtan2SpecialCases() { |
| |
| assertTrue("atan2(NaN, 0.0) should be NaN", Double.isNaN(AccurateMath.atan2(Double.NaN, 0.0))); |
| assertTrue("atan2(0.0, NaN) should be NaN", Double.isNaN(AccurateMath.atan2(0.0, Double.NaN))); |
| |
| final double pinf = Double.POSITIVE_INFINITY; |
| final double ninf = Double.NEGATIVE_INFINITY; |
| // Test using fractions of pi |
| assertAtan2(+0.0, 0.0, 0.0, 1.0); |
| assertAtan2(+0.0, -0.0, 1.0, 1.0); |
| assertAtan2(+0.0, 0.1, 0.0, 1.0); |
| assertAtan2(+0.0, -0.1, 1.0, 1.0); |
| assertAtan2(+0.0, pinf, 0.0, 1.0); |
| assertAtan2(+0.0, ninf, 1.0, 1.0); |
| assertAtan2(-0.0, 0.0, -0.0, 1.0); |
| assertAtan2(-0.0, -0.0, -1.0, 1.0); |
| assertAtan2(-0.0, 0.1, -0.0, 1.0); |
| assertAtan2(-0.0, -0.1, -1.0, 1.0); |
| assertAtan2(-0.0, pinf, -0.0, 1.0); |
| assertAtan2(-0.0, ninf, -1.0, 1.0); |
| assertAtan2(+0.1, 0.0, 1.0, 2.0); |
| assertAtan2(+0.1, -0.0, 1.0, 2.0); |
| assertAtan2(+0.1, 0.1, 1.0, 4.0); |
| assertAtan2(+0.1, -0.1, 3.0, 4.0); |
| assertAtan2(+0.1, pinf, 0.0, 1.0); |
| assertAtan2(+0.1, ninf, 1.0, 1.0); |
| assertAtan2(-0.1, 0.0, -1.0, 2.0); |
| assertAtan2(-0.1, -0.0, -1.0, 2.0); |
| assertAtan2(-0.1, 0.1, -1.0, 4.0); |
| assertAtan2(-0.1, -0.1, -3.0, 4.0); |
| assertAtan2(-0.1, pinf, -0.0, 1.0); |
| assertAtan2(-0.1, ninf, -1.0, 1.0); |
| assertAtan2(pinf, 0.0, 1.0, 2.0); |
| assertAtan2(pinf, -0.0, 1.0, 2.0); |
| assertAtan2(pinf, 0.1, 1.0, 2.0); |
| assertAtan2(pinf, -0.1, 1.0, 2.0); |
| assertAtan2(pinf, pinf, 1.0, 4.0); |
| assertAtan2(pinf, ninf, 3.0, 4.0); |
| assertAtan2(ninf, 0.0, -1.0, 2.0); |
| assertAtan2(ninf, -0.0, -1.0, 2.0); |
| assertAtan2(ninf, 0.1, -1.0, 2.0); |
| assertAtan2(ninf, -0.1, -1.0, 2.0); |
| assertAtan2(ninf, pinf, -1.0, 4.0); |
| assertAtan2(ninf, ninf, -3.0, 4.0); |
| } |
| |
| /** |
| * Assert the atan2(y, x) value is a fraction of pi. |
| * |
| * @param y ordinate |
| * @param x abscissa |
| * @param numerator numerator of the fraction of pi (including the sign) |
| * @param denominator denominator of the fraction of pi |
| */ |
| private static void assertAtan2(double y, double x, double numerator, double denominator) { |
| final double v = AccurateMath.atan2(y, x); |
| if (numerator == 0) { |
| // Exact including the sign. |
| Assertions.assertEquals(numerator, v, |
| () -> String.format("atan2(%s, %s) should be %s but was %s", y, x, numerator, v)); |
| } else { |
| final double expected = AccurateMath.PI * numerator / denominator; |
| Assertions.assertEquals(expected, v, Precision.EPSILON, |
| () -> String.format("atan2(%s, %s) should be pi * %s / %s", y, x, numerator, denominator)); |
| } |
| } |
| |
| @Test |
| public void testPowAccuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < NUMBER_OF_TRIALS; i++) { |
| double x = (generator.nextDouble() * 2.0 + 0.25); |
| double y = (generator.nextDouble() * 1200.0 - 600.0) * generator.nextDouble(); |
| /* |
| * double x = AccurateMath.floor(generator.nextDouble()*1024.0 - 512.0); double |
| * y; if (x != 0) y = AccurateMath.floor(512.0 / AccurateMath.abs(x)); else |
| * y = generator.nextDouble()*1200.0; y = y - y/2; x = AccurateMath.pow(2.0, x) * |
| * generator.nextDouble(); y = y * generator.nextDouble(); |
| */ |
| |
| // double x = generator.nextDouble()*2.0; |
| double tst = AccurateMath.pow(x, y); |
| double ref = DfpMath.pow(field.newDfp(x), field.newDfp(y)).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - |
| Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(DfpMath.pow(field.newDfp(x), field.newDfp(y))).divide(field.newDfp(ulp)).toDouble(); |
| // System.out.println(x + "\t" + y + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("pow() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testExpAccuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < NUMBER_OF_TRIALS; i++) { |
| /* double x = 1.0 + i/1024.0/2.0; */ |
| double x = ((generator.nextDouble() * 1416.0) - 708.0) * generator.nextDouble(); |
| // double x = (generator.nextDouble() * 20.0) - 10.0; |
| // double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble(); |
| /* double x = 3.0 / 512.0 * i - 3.0; */ |
| double tst = AccurateMath.exp(x); |
| double ref = DfpMath.exp(field.newDfp(x)).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - |
| Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(DfpMath.exp(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble(); |
| // System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("exp() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testSinAccuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < NUMBER_OF_TRIALS; i++) { |
| /* double x = 1.0 + i/1024.0/2.0; */ |
| // double x = ((generator.nextDouble() * 1416.0) - 708.0) * generator.nextDouble(); |
| double x = ((generator.nextDouble() * Math.PI) - Math.PI / 2.0) * |
| Math.pow(2, 21) * generator.nextDouble(); |
| // double x = (generator.nextDouble() * 20.0) - 10.0; |
| // double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble(); |
| /* double x = 3.0 / 512.0 * i - 3.0; */ |
| double tst = AccurateMath.sin(x); |
| double ref = DfpMath.sin(field.newDfp(x)).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - |
| Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(DfpMath.sin(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble(); |
| // System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("sin() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testCosAccuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < NUMBER_OF_TRIALS; i++) { |
| /* double x = 1.0 + i/1024.0/2.0; */ |
| // double x = ((generator.nextDouble() * 1416.0) - 708.0) * generator.nextDouble(); |
| double x = ((generator.nextDouble() * Math.PI) - Math.PI / 2.0) * |
| Math.pow(2, 21) * generator.nextDouble(); |
| // double x = (generator.nextDouble() * 20.0) - 10.0; |
| // double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble(); |
| /* double x = 3.0 / 512.0 * i - 3.0; */ |
| double tst = AccurateMath.cos(x); |
| double ref = DfpMath.cos(field.newDfp(x)).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - |
| Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(DfpMath.cos(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble(); |
| // System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("cos() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testTanAccuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < NUMBER_OF_TRIALS; i++) { |
| /* double x = 1.0 + i/1024.0/2.0; */ |
| // double x = ((generator.nextDouble() * 1416.0) - 708.0) * generator.nextDouble(); |
| double x = ((generator.nextDouble() * Math.PI) - Math.PI / 2.0) * |
| Math.pow(2, 12) * generator.nextDouble(); |
| // double x = (generator.nextDouble() * 20.0) - 10.0; |
| // double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble(); |
| /* double x = 3.0 / 512.0 * i - 3.0; */ |
| double tst = AccurateMath.tan(x); |
| double ref = DfpMath.tan(field.newDfp(x)).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - |
| Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(DfpMath.tan(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble(); |
| // System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("tan() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testAtanAccuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < NUMBER_OF_TRIALS; i++) { |
| /* double x = 1.0 + i/1024.0/2.0; */ |
| // double x = ((generator.nextDouble() * 1416.0) - 708.0) * generator.nextDouble(); |
| // double x = ((generator.nextDouble() * Math.PI) - Math.PI/2.0) * |
| // generator.nextDouble(); |
| double x = ((generator.nextDouble() * 16.0) - 8.0) * generator.nextDouble(); |
| |
| // double x = (generator.nextDouble() * 20.0) - 10.0; |
| // double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble(); |
| /* double x = 3.0 / 512.0 * i - 3.0; */ |
| double tst = AccurateMath.atan(x); |
| double ref = DfpMath.atan(field.newDfp(x)).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - |
| Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(DfpMath.atan(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble(); |
| // System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("atan() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testAtan2Accuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < NUMBER_OF_TRIALS; i++) { |
| /* double x = 1.0 + i/1024.0/2.0; */ |
| // double x = ((generator.nextDouble() * 1416.0) - 708.0) * generator.nextDouble(); |
| double x = generator.nextDouble() - 0.5; |
| double y = generator.nextDouble() - 0.5; |
| // double x = (generator.nextDouble() * 20.0) - 10.0; |
| // double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble(); |
| /* double x = 3.0 / 512.0 * i - 3.0; */ |
| double tst = AccurateMath.atan2(y, x); |
| Dfp refdfp = DfpMath.atan(field.newDfp(y).divide(field.newDfp(x))); |
| /* Make adjustments for sign */ |
| if (x < 0.0) { |
| if (y > 0.0) { |
| refdfp = field.getPi().add(refdfp); |
| } else { |
| refdfp = refdfp.subtract(field.getPi()); |
| } |
| } |
| |
| double ref = refdfp.toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - |
| Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(refdfp).divide(field.newDfp(ulp)).toDouble(); |
| // System.out.println(x + "\t" + y + "\t" + tst + "\t" + ref + "\t" + errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("atan2() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testExpm1Accuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < NUMBER_OF_TRIALS; i++) { |
| /* double x = 1.0 + i/1024.0/2.0; */ |
| // double x = (generator.nextDouble() * 20.0) - 10.0; |
| double x = ((generator.nextDouble() * 16.0) - 8.0) * generator.nextDouble(); |
| /* double x = 3.0 / 512.0 * i - 3.0; */ |
| double tst = AccurateMath.expm1(x); |
| double ref = DfpMath.exp(field.newDfp(x)).subtract(field.getOne()).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - |
| Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(DfpMath.exp(field.newDfp(x)).subtract(field.getOne())).divide(field.newDfp(ulp)).toDouble(); |
| // System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("expm1() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testAsinAccuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < 10000; i++) { |
| double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble(); |
| |
| double tst = AccurateMath.asin(x); |
| double ref = DfpMath.asin(field.newDfp(x)).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(DfpMath.asin(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble(); |
| //System.out.println(x+"\t"+tst+"\t"+ref+"\t"+err+"\t"+errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("asin() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testAcosAccuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < 10000; i++) { |
| double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble(); |
| |
| double tst = AccurateMath.acos(x); |
| double ref = DfpMath.acos(field.newDfp(x)).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(DfpMath.acos(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble(); |
| //System.out.println(x+"\t"+tst+"\t"+ref+"\t"+err+"\t"+errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("acos() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| /** |
| * Added tests for a 100% coverage of acos(). |
| */ |
| @Test |
| public void testAcosSpecialCases() { |
| |
| assertTrue("acos(NaN) should be NaN", Double.isNaN(AccurateMath.acos(Double.NaN))); |
| assertTrue("acos(-1.1) should be NaN", Double.isNaN(AccurateMath.acos(-1.1))); |
| assertTrue("acos(-1.1) should be NaN", Double.isNaN(AccurateMath.acos(1.1))); |
| assertEquals("acos(-1.0) should be PI", AccurateMath.acos(-1.0), AccurateMath.PI, Precision.EPSILON); |
| assertEquals("acos(1.0) should be 0.0", AccurateMath.acos(1.0), 0.0, Precision.EPSILON); |
| assertEquals("acos(0.0) should be PI/2", AccurateMath.acos(0.0), AccurateMath.PI / 2.0, Precision.EPSILON); |
| } |
| |
| /** |
| * Added tests for a 100% coverage of asin(). |
| */ |
| @Test |
| public void testAsinSpecialCases() { |
| |
| assertTrue("asin(NaN) should be NaN", Double.isNaN(AccurateMath.asin(Double.NaN))); |
| assertTrue("asin(1.1) should be NaN", Double.isNaN(AccurateMath.asin(1.1))); |
| assertTrue("asin(-1.1) should be NaN", Double.isNaN(AccurateMath.asin(-1.1))); |
| assertEquals("asin(1.0) should be PI/2", AccurateMath.asin(1.0), AccurateMath.PI / 2.0, Precision.EPSILON); |
| assertEquals("asin(-1.0) should be -PI/2", AccurateMath.asin(-1.0), -AccurateMath.PI / 2.0, Precision.EPSILON); |
| assertEquals("asin(0.0) should be 0.0", AccurateMath.asin(0.0), 0.0, Precision.EPSILON); |
| } |
| |
| private Dfp cosh(Dfp x) { |
| return DfpMath.exp(x).add(DfpMath.exp(x.negate())).divide(2); |
| } |
| |
| private Dfp sinh(Dfp x) { |
| return DfpMath.exp(x).subtract(DfpMath.exp(x.negate())).divide(2); |
| } |
| |
| private Dfp tanh(Dfp x) { |
| return sinh(x).divide(cosh(x)); |
| } |
| |
| @Test |
| public void testSinhAccuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < 10000; i++) { |
| double x = ((generator.nextDouble() * 16.0) - 8.0) * generator.nextDouble(); |
| |
| double tst = AccurateMath.sinh(x); |
| double ref = sinh(field.newDfp(x)).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(sinh(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble(); |
| //System.out.println(x+"\t"+tst+"\t"+ref+"\t"+err+"\t"+errulp); |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("sinh() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testCoshAccuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < 10000; i++) { |
| double x = ((generator.nextDouble() * 16.0) - 8.0) * generator.nextDouble(); |
| |
| double tst = AccurateMath.cosh(x); |
| double ref = cosh(field.newDfp(x)).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(cosh(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble(); |
| //System.out.println(x+"\t"+tst+"\t"+ref+"\t"+err+"\t"+errulp); |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("cosh() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testTanhAccuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < 10000; i++) { |
| double x = ((generator.nextDouble() * 16.0) - 8.0) * generator.nextDouble(); |
| |
| double tst = AccurateMath.tanh(x); |
| double ref = tanh(field.newDfp(x)).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(tanh(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble(); |
| //System.out.println(x+"\t"+tst+"\t"+ref+"\t"+err+"\t"+errulp); |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("tanh() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testCbrtAccuracy() { |
| double maxerrulp = 0.0; |
| |
| for (int i = 0; i < 10000; i++) { |
| double x = ((generator.nextDouble() * 200.0) - 100.0) * generator.nextDouble(); |
| |
| double tst = AccurateMath.cbrt(x); |
| double ref = cbrt(field.newDfp(x)).toDouble(); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(cbrt(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble(); |
| //System.out.println(x+"\t"+tst+"\t"+ref+"\t"+err+"\t"+errulp); |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("cbrt() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| private Dfp cbrt(Dfp x) { |
| boolean negative = false; |
| |
| if (x.lessThan(field.getZero())) { |
| negative = true; |
| x = x.negate(); |
| } |
| |
| Dfp y = DfpMath.pow(x, field.getOne().divide(3)); |
| |
| if (negative) { |
| y = y.negate(); |
| } |
| |
| return y; |
| } |
| |
| @Test |
| public void testToDegrees() { |
| double maxerrulp = 0.0; |
| for (int i = 0; i < NUMBER_OF_TRIALS; i++) { |
| double x = generator.nextDouble(); |
| double tst = field.newDfp(x).multiply(180).divide(field.getPi()).toDouble(); |
| double ref = AccurateMath.toDegrees(x); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - |
| Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(DfpMath.exp(field.newDfp(x)).subtract(field.getOne())).divide(field.newDfp(ulp)).toDouble(); |
| // System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| assertTrue("toDegrees() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testToRadians() { |
| double maxerrulp = 0.0; |
| for (int i = 0; i < NUMBER_OF_TRIALS; i++) { |
| double x = generator.nextDouble(); |
| double tst = field.newDfp(x).multiply(field.getPi()).divide(180).toDouble(); |
| double ref = AccurateMath.toRadians(x); |
| double err = (tst - ref) / ref; |
| |
| if (err != 0) { |
| double ulp = Math.abs(ref - |
| Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1))); |
| double errulp = field.newDfp(tst).subtract(DfpMath.exp(field.newDfp(x)).subtract(field.getOne())).divide(field.newDfp(ulp)).toDouble(); |
| // System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp); |
| |
| maxerrulp = Math.max(maxerrulp, Math.abs(errulp)); |
| } |
| } |
| |
| assertTrue("toRadians() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP); |
| } |
| |
| @Test |
| public void testNextAfter() { |
| // 0x402fffffffffffff 0x404123456789abcd -> 4030000000000000 |
| assertEquals(16.0, AccurateMath.nextUp(15.999999999999998), 0.0); |
| |
| // 0xc02fffffffffffff 0x404123456789abcd -> c02ffffffffffffe |
| assertEquals(-15.999999999999996, AccurateMath.nextAfter(-15.999999999999998, 34.27555555555555), 0.0); |
| |
| // 0x402fffffffffffff 0x400123456789abcd -> 402ffffffffffffe |
| assertEquals(15.999999999999996, AccurateMath.nextDown(15.999999999999998), 0.0); |
| |
| // 0xc02fffffffffffff 0x400123456789abcd -> c02ffffffffffffe |
| assertEquals(-15.999999999999996, AccurateMath.nextAfter(-15.999999999999998, 2.142222222222222), 0.0); |
| |
| // 0x4020000000000000 0x404123456789abcd -> 4020000000000001 |
| assertEquals(8.000000000000002, AccurateMath.nextAfter(8.0, 34.27555555555555), 0.0); |
| |
| // 0xc020000000000000 0x404123456789abcd -> c01fffffffffffff |
| assertEquals(-7.999999999999999, AccurateMath.nextAfter(-8.0, 34.27555555555555), 0.0); |
| |
| // 0x4020000000000000 0x400123456789abcd -> 401fffffffffffff |
| assertEquals(7.999999999999999, AccurateMath.nextAfter(8.0, 2.142222222222222), 0.0); |
| |
| // 0xc020000000000000 0x400123456789abcd -> c01fffffffffffff |
| assertEquals(-7.999999999999999, AccurateMath.nextAfter(-8.0, 2.142222222222222), 0.0); |
| |
| // 0x3f2e43753d36a223 0x3f2e43753d36a224 -> 3f2e43753d36a224 |
| assertEquals(2.308922399667661E-4, AccurateMath.nextAfter(2.3089223996676606E-4, 2.308922399667661E-4), 0.0); |
| |
| // 0x3f2e43753d36a223 0x3f2e43753d36a223 -> 3f2e43753d36a223 |
| assertEquals(2.3089223996676606E-4, AccurateMath.nextAfter(2.3089223996676606E-4, 2.3089223996676606E-4), 0.0); |
| |
| // 0x3f2e43753d36a223 0x3f2e43753d36a222 -> 3f2e43753d36a222 |
| assertEquals(2.3089223996676603E-4, AccurateMath.nextAfter(2.3089223996676606E-4, 2.3089223996676603E-4), 0.0); |
| |
| // 0x3f2e43753d36a223 0xbf2e43753d36a224 -> 3f2e43753d36a222 |
| assertEquals(2.3089223996676603E-4, AccurateMath.nextAfter(2.3089223996676606E-4, -2.308922399667661E-4), 0.0); |
| |
| // 0x3f2e43753d36a223 0xbf2e43753d36a223 -> 3f2e43753d36a222 |
| assertEquals(2.3089223996676603E-4, AccurateMath.nextAfter(2.3089223996676606E-4, -2.3089223996676606E-4), 0.0); |
| |
| // 0x3f2e43753d36a223 0xbf2e43753d36a222 -> 3f2e43753d36a222 |
| assertEquals(2.3089223996676603E-4, AccurateMath.nextAfter(2.3089223996676606E-4, -2.3089223996676603E-4), 0.0); |
| |
| // 0xbf2e43753d36a223 0x3f2e43753d36a224 -> bf2e43753d36a222 |
| assertEquals(-2.3089223996676603E-4, AccurateMath.nextAfter(-2.3089223996676606E-4, 2.308922399667661E-4), 0.0); |
| |
| // 0xbf2e43753d36a223 0x3f2e43753d36a223 -> bf2e43753d36a222 |
| assertEquals(-2.3089223996676603E-4, AccurateMath.nextAfter(-2.3089223996676606E-4, 2.3089223996676606E-4), 0.0); |
| |
| // 0xbf2e43753d36a223 0x3f2e43753d36a222 -> bf2e43753d36a222 |
| assertEquals(-2.3089223996676603E-4, AccurateMath.nextAfter(-2.3089223996676606E-4, 2.3089223996676603E-4), 0.0); |
| |
| // 0xbf2e43753d36a223 0xbf2e43753d36a224 -> bf2e43753d36a224 |
| assertEquals(-2.308922399667661E-4, AccurateMath.nextAfter(-2.3089223996676606E-4, -2.308922399667661E-4), 0.0); |
| |
| // 0xbf2e43753d36a223 0xbf2e43753d36a223 -> bf2e43753d36a223 |
| assertEquals(-2.3089223996676606E-4, AccurateMath.nextAfter(-2.3089223996676606E-4, -2.3089223996676606E-4), 0.0); |
| |
| // 0xbf2e43753d36a223 0xbf2e43753d36a222 -> bf2e43753d36a222 |
| assertEquals(-2.3089223996676603E-4, AccurateMath.nextAfter(-2.3089223996676606E-4, -2.3089223996676603E-4), 0.0); |
| |
| } |
| |
| @Test |
| public void testDoubleNextAfterSpecialCases() { |
| assertEquals(-Double.MAX_VALUE, AccurateMath.nextAfter(Double.NEGATIVE_INFINITY, 0D), 0D); |
| assertEquals(Double.MAX_VALUE, AccurateMath.nextAfter(Double.POSITIVE_INFINITY, 0D), 0D); |
| assertEquals(Double.NaN, AccurateMath.nextAfter(Double.NaN, 0D), 0D); |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.nextAfter(Double.MAX_VALUE, Double.POSITIVE_INFINITY), 0D); |
| assertEquals(Double.NEGATIVE_INFINITY, AccurateMath.nextAfter(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY), 0D); |
| assertEquals(Double.MIN_VALUE, AccurateMath.nextAfter(0D, 1D), 0D); |
| assertEquals(-Double.MIN_VALUE, AccurateMath.nextAfter(0D, -1D), 0D); |
| assertEquals(0D, AccurateMath.nextAfter(Double.MIN_VALUE, -1), 0D); |
| assertEquals(0D, AccurateMath.nextAfter(-Double.MIN_VALUE, 1), 0D); |
| } |
| |
| @Test |
| public void testFloatNextAfterSpecialCases() { |
| assertEquals(-Float.MAX_VALUE, AccurateMath.nextAfter(Float.NEGATIVE_INFINITY, 0F), 0F); |
| assertEquals(Float.MAX_VALUE, AccurateMath.nextAfter(Float.POSITIVE_INFINITY, 0F), 0F); |
| assertEquals(Float.NaN, AccurateMath.nextAfter(Float.NaN, 0F), 0F); |
| assertEquals(Float.POSITIVE_INFINITY, AccurateMath.nextUp(Float.MAX_VALUE), 0F); |
| assertEquals(Float.NEGATIVE_INFINITY, AccurateMath.nextDown(-Float.MAX_VALUE), 0F); |
| assertEquals(Float.MIN_VALUE, AccurateMath.nextAfter(0F, 1F), 0F); |
| assertEquals(-Float.MIN_VALUE, AccurateMath.nextAfter(0F, -1F), 0F); |
| assertEquals(0F, AccurateMath.nextAfter(Float.MIN_VALUE, -1F), 0F); |
| assertEquals(0F, AccurateMath.nextAfter(-Float.MIN_VALUE, 1F), 0F); |
| } |
| |
| @Test |
| public void testDoubleScalbSpecialCases() { |
| assertEquals(2.5269841324701218E-175, AccurateMath.scalb(2.2250738585072014E-308, 442), 0D); |
| assertEquals(1.307993905256674E297, AccurateMath.scalb(1.1102230246251565E-16, 1040), 0D); |
| assertEquals(7.2520887996488946E-217, AccurateMath.scalb(Double.MIN_VALUE, 356), 0D); |
| assertEquals(8.98846567431158E307, AccurateMath.scalb(Double.MIN_VALUE, 2097), 0D); |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.scalb(Double.MIN_VALUE, 2098), 0D); |
| assertEquals(1.1125369292536007E-308, AccurateMath.scalb(2.225073858507201E-308, -1), 0D); |
| assertEquals(1.0E-323, AccurateMath.scalb(Double.MAX_VALUE, -2097), 0D); |
| assertEquals(Double.MIN_VALUE, AccurateMath.scalb(Double.MAX_VALUE, -2098), 0D); |
| assertEquals(0, AccurateMath.scalb(Double.MAX_VALUE, -2099), 0D); |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.scalb(Double.POSITIVE_INFINITY, -1000000), 0D); |
| assertEquals(Double.NEGATIVE_INFINITY, AccurateMath.scalb(-1.1102230246251565E-16, 1078), 0D); |
| assertEquals(Double.NEGATIVE_INFINITY, AccurateMath.scalb(-1.1102230246251565E-16, 1079), 0D); |
| assertEquals(Double.NEGATIVE_INFINITY, AccurateMath.scalb(-2.2250738585072014E-308, 2047), 0D); |
| assertEquals(Double.NEGATIVE_INFINITY, AccurateMath.scalb(-2.2250738585072014E-308, 2048), 0D); |
| assertEquals(Double.NEGATIVE_INFINITY, AccurateMath.scalb(-1.7976931348623157E308, 2147483647), 0D); |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.scalb(+1.7976931348623157E308, 2147483647), 0D); |
| assertEquals(Double.NEGATIVE_INFINITY, AccurateMath.scalb(-1.1102230246251565E-16, 2147483647), 0D); |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.scalb(+1.1102230246251565E-16, 2147483647), 0D); |
| assertEquals(Double.NEGATIVE_INFINITY, AccurateMath.scalb(-2.2250738585072014E-308, 2147483647), 0D); |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.scalb(+2.2250738585072014E-308, 2147483647), 0D); |
| } |
| |
| @Test |
| public void testFloatScalbSpecialCases() { |
| assertEquals(0f, AccurateMath.scalb(Float.MIN_VALUE, -30), 0F); |
| assertEquals(2 * Float.MIN_VALUE, AccurateMath.scalb(Float.MIN_VALUE, 1), 0F); |
| assertEquals(7.555786e22f, AccurateMath.scalb(Float.MAX_VALUE, -52), 0F); |
| assertEquals(1.7014118e38f, AccurateMath.scalb(Float.MIN_VALUE, 276), 0F); |
| assertEquals(Float.POSITIVE_INFINITY, AccurateMath.scalb(Float.MIN_VALUE, 277), 0F); |
| assertEquals(5.8774718e-39f, AccurateMath.scalb(1.1754944e-38f, -1), 0F); |
| assertEquals(2 * Float.MIN_VALUE, AccurateMath.scalb(Float.MAX_VALUE, -276), 0F); |
| assertEquals(Float.MIN_VALUE, AccurateMath.scalb(Float.MAX_VALUE, -277), 0F); |
| assertEquals(0, AccurateMath.scalb(Float.MAX_VALUE, -278), 0F); |
| assertEquals(Float.POSITIVE_INFINITY, AccurateMath.scalb(Float.POSITIVE_INFINITY, -1000000), 0F); |
| assertEquals(-3.13994498e38f, AccurateMath.scalb(-1.1e-7f, 151), 0F); |
| assertEquals(Float.NEGATIVE_INFINITY, AccurateMath.scalb(-1.1e-7f, 152), 0F); |
| assertEquals(Float.POSITIVE_INFINITY, AccurateMath.scalb(3.4028235E38f, 2147483647), 0F); |
| assertEquals(Float.NEGATIVE_INFINITY, AccurateMath.scalb(-3.4028235E38f, 2147483647), 0F); |
| } |
| |
| @Test |
| public void testSignumDouble() { |
| final double delta = 0.0; |
| assertEquals(1.0, AccurateMath.signum(2.0), delta); |
| assertEquals(0.0, AccurateMath.signum(0.0), delta); |
| assertEquals(-1.0, AccurateMath.signum(-2.0), delta); |
| Assert.assertTrue(Double.isNaN(AccurateMath.signum(Double.NaN))); |
| } |
| |
| @Test |
| public void testSignumFloat() { |
| final float delta = 0.0F; |
| assertEquals(1.0F, AccurateMath.signum(2.0F), delta); |
| assertEquals(0.0F, AccurateMath.signum(0.0F), delta); |
| assertEquals(-1.0F, AccurateMath.signum(-2.0F), delta); |
| Assert.assertTrue(Double.isNaN(AccurateMath.signum(Float.NaN))); |
| } |
| |
| @Test |
| public void testLogWithBase() { |
| assertEquals(2.0, AccurateMath.log(2, 4), 0); |
| assertEquals(3.0, AccurateMath.log(2, 8), 0); |
| assertTrue(Double.isNaN(AccurateMath.log(-1, 1))); |
| assertTrue(Double.isNaN(AccurateMath.log(1, -1))); |
| assertTrue(Double.isNaN(AccurateMath.log(0, 0))); |
| assertEquals(0, AccurateMath.log(0, 10), 0); |
| assertEquals(Double.NEGATIVE_INFINITY, AccurateMath.log(10, 0), 0); |
| } |
| |
| @Test |
| public void testIndicatorDouble() { |
| double delta = 0.0; |
| assertEquals(1.0, AccurateMath.copySign(1d, 2.0), delta); |
| assertEquals(1.0, AccurateMath.copySign(1d, 0.0), delta); |
| assertEquals(-1.0, AccurateMath.copySign(1d, -0.0), delta); |
| assertEquals(1.0, AccurateMath.copySign(1d, Double.POSITIVE_INFINITY), delta); |
| assertEquals(-1.0, AccurateMath.copySign(1d, Double.NEGATIVE_INFINITY), delta); |
| assertEquals(1.0, AccurateMath.copySign(1d, Double.NaN), delta); |
| assertEquals(-1.0, AccurateMath.copySign(1d, -2.0), delta); |
| } |
| |
| @Test |
| public void testIndicatorFloat() { |
| float delta = 0.0F; |
| assertEquals(1.0F, AccurateMath.copySign(1d, 2.0F), delta); |
| assertEquals(1.0F, AccurateMath.copySign(1d, 0.0F), delta); |
| assertEquals(-1.0F, AccurateMath.copySign(1d, -0.0F), delta); |
| assertEquals(1.0F, AccurateMath.copySign(1d, Float.POSITIVE_INFINITY), delta); |
| assertEquals(-1.0F, AccurateMath.copySign(1d, Float.NEGATIVE_INFINITY), delta); |
| assertEquals(1.0F, AccurateMath.copySign(1d, Float.NaN), delta); |
| assertEquals(-1.0F, AccurateMath.copySign(1d, -2.0F), delta); |
| } |
| |
| @Test |
| public void testIntPow() { |
| final int maxExp = 300; |
| DfpField localField = new DfpField(40); |
| final double base = 1.23456789; |
| Dfp baseDfp = localField.newDfp(base); |
| Dfp dfpPower = localField.getOne(); |
| for (int i = 0; i < maxExp; i++) { |
| assertEquals("exp=" + i, dfpPower.toDouble(), AccurateMath.pow(base, i), |
| 0.6 * AccurateMath.ulp(dfpPower.toDouble())); |
| dfpPower = dfpPower.multiply(baseDfp); |
| } |
| } |
| |
| @Test |
| public void testIntPowHuge() { |
| assertTrue(Double.isInfinite(AccurateMath.pow(AccurateMath.scalb(1.0, 500), 4))); |
| } |
| |
| @Test(timeout = 5000L) // This test must finish in finite time. |
| public void testIntPowLongMinValue() { |
| assertEquals(1.0, AccurateMath.pow(1.0, Long.MIN_VALUE), -1.0); |
| } |
| |
| @Test(timeout = 5000L) |
| public void testIntPowSpecialCases() { |
| final double EXACT = -1.0; |
| final double[] DOUBLES = new double[] { |
| Double.NEGATIVE_INFINITY, -0.0, Double.NaN, 0.0, Double.POSITIVE_INFINITY, |
| Long.MIN_VALUE, Integer.MIN_VALUE, Short.MIN_VALUE, Byte.MIN_VALUE, |
| -(double)Long.MIN_VALUE, -(double)Integer.MIN_VALUE, -(double)Short.MIN_VALUE, -(double)Byte.MIN_VALUE, |
| Byte.MAX_VALUE, Short.MAX_VALUE, Integer.MAX_VALUE, Long.MAX_VALUE, |
| -Byte.MAX_VALUE, -Short.MAX_VALUE, -Integer.MAX_VALUE, -Long.MAX_VALUE, |
| Float.MAX_VALUE, Double.MAX_VALUE, Double.MIN_VALUE, Float.MIN_VALUE, |
| -Float.MAX_VALUE, -Double.MAX_VALUE, -Double.MIN_VALUE, -Float.MIN_VALUE, |
| 0.5, 0.1, 0.2, 0.8, 1.1, 1.2, 1.5, 1.8, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 1.3, 2.2, 2.5, 2.8, 33.0, 33.1, 33.5, 33.8, 10.0, 300.0, 400.0, 500.0, |
| -0.5, -0.1, -0.2, -0.8, -1.1, -1.2, -1.5, -1.8, -1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0, -9.0, -1.3, -2.2, -2.5, -2.8, -33.0, -33.1, -33.5, -33.8, -10.0, -300.0, -400.0, -500.0 |
| }; |
| |
| final long[] INTS = new long[]{Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 2, Integer.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 2, 0, 1, 2, 3, 5, 8, 10, 20, 100, 300, 500, -1, -2, -3, -5, -8, -10, -20, -100, -300, -500}; |
| // Special cases from Math.pow javadoc: |
| // If the second argument is positive or negative zero, then the result is 1.0. |
| for (double d : DOUBLES) { |
| assertEquals(1.0, AccurateMath.pow(d, 0L), EXACT); |
| } |
| // If the second argument is 1.0, then the result is the same as the first argument. |
| for (double d : DOUBLES) { |
| assertEquals(d, AccurateMath.pow(d, 1L), EXACT); |
| } |
| // If the second argument is NaN, then the result is NaN. <- Impossible with int. |
| // If the first argument is NaN and the second argument is nonzero, then the result is NaN. |
| for (long i : INTS) { |
| if (i != 0L) { |
| assertEquals(Double.NaN, AccurateMath.pow(Double.NaN, i), EXACT); |
| } |
| } |
| // If the absolute value of the first argument is greater than 1 and the second argument is positive infinity, or |
| // the absolute value of the first argument is less than 1 and the second argument is negative infinity, then the result is positive infinity. |
| for (double d : DOUBLES) { |
| if (Math.abs(d) > 1.0) { |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.pow(d, Long.MAX_VALUE - 1L), EXACT); |
| } |
| } |
| for (double d : DOUBLES) { |
| if (Math.abs(d) < 1.0) { |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.pow(d, Long.MIN_VALUE), EXACT); |
| } |
| } |
| // Note: Long.MAX_VALUE isn't actually an infinity, so its parity affects the sign of resulting infinity. |
| for (double d : DOUBLES) { |
| if (Math.abs(d) > 1.0) { |
| assertTrue(Double.isInfinite(AccurateMath.pow(d, Long.MAX_VALUE))); |
| } |
| } |
| for (double d : DOUBLES) { |
| if (Math.abs(d) < 1.0) { |
| assertTrue(Double.isInfinite(AccurateMath.pow(d, Long.MIN_VALUE + 1L))); |
| } |
| } |
| // If the absolute value of the first argument is greater than 1 and the second argument is negative infinity, or |
| // the absolute value of the first argument is less than 1 and the second argument is positive infinity, then the result is positive zero. |
| for (double d : DOUBLES) { |
| if (Math.abs(d) > 1.0) { |
| assertEquals(0.0, AccurateMath.pow(d, Long.MIN_VALUE), EXACT); |
| } |
| } |
| for (double d : DOUBLES) { |
| if (Math.abs(d) < 1.0) { |
| assertEquals(0.0, AccurateMath.pow(d, Long.MAX_VALUE - 1L), EXACT); |
| } |
| } |
| // Note: Long.MAX_VALUE isn't actually an infinity, so its parity affects the sign of resulting zero. |
| for (double d : DOUBLES) { |
| if (Math.abs(d) > 1.0) { |
| assertEquals(0.0, AccurateMath.pow(d, Long.MIN_VALUE + 1L), 0.0); |
| } |
| } |
| for (double d : DOUBLES) { |
| if (Math.abs(d) < 1.0) { |
| assertEquals(0.0, AccurateMath.pow(d, Long.MAX_VALUE), 0.0); |
| } |
| } |
| // If the absolute value of the first argument equals 1 and the second argument is infinite, then the result is NaN. <- Impossible with int. |
| // If the first argument is positive zero and the second argument is greater than zero, or |
| // the first argument is positive infinity and the second argument is less than zero, then the result is positive zero. |
| for (long i : INTS) { |
| if (i > 0L) { |
| assertEquals(0.0, AccurateMath.pow(0.0, i), EXACT); |
| } |
| } |
| for (long i : INTS) { |
| if (i < 0L) { |
| assertEquals(0.0, AccurateMath.pow(Double.POSITIVE_INFINITY, i), EXACT); |
| } |
| } |
| // If the first argument is positive zero and the second argument is less than zero, or |
| // the first argument is positive infinity and the second argument is greater than zero, then the result is positive infinity. |
| for (long i : INTS) { |
| if (i < 0L) { |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.pow(0.0, i), EXACT); |
| } |
| } |
| for (long i : INTS) { |
| if (i > 0L) { |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.pow(Double.POSITIVE_INFINITY, i), EXACT); |
| } |
| } |
| // If the first argument is negative zero and the second argument is greater than zero but not a finite odd integer, or |
| // the first argument is negative infinity and the second argument is less than zero but not a finite odd integer, then the result is positive zero. |
| for (long i : INTS) { |
| if (i > 0L && (i & 1L) == 0L) { |
| assertEquals(0.0, AccurateMath.pow(-0.0, i), EXACT); |
| } |
| } |
| for (long i : INTS) { |
| if (i < 0L && (i & 1L) == 0L) { |
| assertEquals(0.0, AccurateMath.pow(Double.NEGATIVE_INFINITY, i), EXACT); |
| } |
| } |
| // If the first argument is negative zero and the second argument is a positive finite odd integer, or |
| // the first argument is negative infinity and the second argument is a negative finite odd integer, then the result is negative zero. |
| for (long i : INTS) { |
| if (i > 0L && (i & 1L) == 1L) { |
| assertEquals(-0.0, AccurateMath.pow(-0.0, i), EXACT); |
| } |
| } |
| for (long i : INTS) { |
| if (i < 0L && (i & 1L) == 1L) { |
| assertEquals(-0.0, AccurateMath.pow(Double.NEGATIVE_INFINITY, i), EXACT); |
| } |
| } |
| // If the first argument is negative zero and the second argument is less than zero but not a finite odd integer, or |
| // the first argument is negative infinity and the second argument is greater than zero but not a finite odd integer, then the result is positive infinity. |
| for (long i : INTS) { |
| if (i > 0L && (i & 1L) == 0L) { |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.pow(Double.NEGATIVE_INFINITY, i), EXACT); |
| } |
| } |
| for (long i : INTS) { |
| if (i < 0L && (i & 1L) == 0L) { |
| assertEquals(Double.POSITIVE_INFINITY, AccurateMath.pow(-0.0, i), EXACT); |
| } |
| } |
| // If the first argument is negative zero and the second argument is a negative finite odd integer, or |
| // the first argument is negative infinity and the second argument is a positive finite odd integer, then the result is negative infinity. |
| for (long i : INTS) { |
| if (i > 0L && (i & 1L) == 1L) { |
| assertEquals(Double.NEGATIVE_INFINITY, AccurateMath.pow(Double.NEGATIVE_INFINITY, i), EXACT); |
| } |
| } |
| for (long i : INTS) { |
| if (i < 0L && (i & 1L) == 1L) { |
| assertEquals(Double.NEGATIVE_INFINITY, AccurateMath.pow(-0.0, i), EXACT); |
| } |
| } |
| for (double d : DOUBLES) { |
| // If the first argument is finite and less than zero |
| if (d < 0.0 && Math.abs(d) <= Double.MAX_VALUE) { |
| for (long i : INTS) { |
| // if the second argument is a finite even integer, the result is equal to the result of raising the absolute value of the first argument to the power of the second argument |
| if ((i & 1L) == 0L) { |
| assertEquals(AccurateMath.pow(-d, i), AccurateMath.pow(d, i), EXACT); |
| } else { |
| // if the second argument is a finite odd integer, the result is equal to the negative of the result of raising the absolute value of the first argument to the power of the second argument |
| assertEquals(-AccurateMath.pow(-d, i), AccurateMath.pow(d, i), EXACT); |
| } |
| // if the second argument is finite and not an integer, then the result is NaN. <- Impossible with int. |
| } |
| } |
| } |
| // If both arguments are integers, then the result is exactly equal to the mathematical result of raising the first argument to the power |
| // of the second argument if that result can in fact be represented exactly as a double value. <- Other tests. |
| } |
| |
| @Test |
| public void testIncrementExactInt() { |
| int[] specialValues = new int[] { |
| Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 2, |
| Integer.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 2, |
| -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
| -1 - (Integer.MIN_VALUE / 2), 0 - (Integer.MIN_VALUE / 2), 1 - (Integer.MIN_VALUE / 2), |
| -1 + (Integer.MAX_VALUE / 2), 0 + (Integer.MAX_VALUE / 2), 1 + (Integer.MAX_VALUE / 2), |
| }; |
| for (int a : specialValues) { |
| BigInteger bdA = BigInteger.valueOf(a); |
| BigInteger bdSum = bdA.add(BigInteger.ONE); |
| if (bdSum.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || |
| bdSum.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { |
| try { |
| AccurateMath.incrementExact(a); |
| Assert.fail("an exception should have been thrown"); |
| } catch (ArithmeticException mae) { |
| // expected |
| } |
| } else { |
| assertEquals(bdSum, BigInteger.valueOf(AccurateMath.incrementExact(a))); |
| } |
| } |
| } |
| |
| @Test |
| public void testDecrementExactInt() { |
| int[] specialValues = new int[] { |
| Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 2, |
| Integer.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 2, |
| -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
| -1 - (Integer.MIN_VALUE / 2), 0 - (Integer.MIN_VALUE / 2), 1 - (Integer.MIN_VALUE / 2), |
| -1 + (Integer.MAX_VALUE / 2), 0 + (Integer.MAX_VALUE / 2), 1 + (Integer.MAX_VALUE / 2), |
| }; |
| for (int a : specialValues) { |
| BigInteger bdA = BigInteger.valueOf(a); |
| BigInteger bdSub = bdA.subtract(BigInteger.ONE); |
| if (bdSub.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || |
| bdSub.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { |
| try { |
| AccurateMath.decrementExact(a); |
| fail("an exception should have been thrown"); |
| } catch (ArithmeticException mae) { |
| // expected |
| } |
| } else { |
| assertEquals(bdSub, BigInteger.valueOf(AccurateMath.decrementExact(a))); |
| } |
| } |
| } |
| |
| @Test |
| public void testAddExactInt() { |
| int[] specialValues = new int[] { |
| Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 2, |
| Integer.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 2, |
| -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
| -1 - (Integer.MIN_VALUE / 2), 0 - (Integer.MIN_VALUE / 2), 1 - (Integer.MIN_VALUE / 2), |
| -1 + (Integer.MAX_VALUE / 2), 0 + (Integer.MAX_VALUE / 2), 1 + (Integer.MAX_VALUE / 2), |
| }; |
| for (int a : specialValues) { |
| for (int b : specialValues) { |
| BigInteger bdA = BigInteger.valueOf(a); |
| BigInteger bdB = BigInteger.valueOf(b); |
| BigInteger bdSum = bdA.add(bdB); |
| if (bdSum.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || |
| bdSum.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { |
| try { |
| AccurateMath.addExact(a, b); |
| fail("an exception should have been thrown"); |
| } catch (ArithmeticException mae) { |
| // expected |
| } |
| } else { |
| assertEquals(bdSum, BigInteger.valueOf(AccurateMath.addExact(a, b))); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testAddExactLong() { |
| long[] specialValues = new long[] { |
| Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 2, |
| Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE - 2, |
| -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
| -1 - (Long.MIN_VALUE / 2), 0 - (Long.MIN_VALUE / 2), 1 - (Long.MIN_VALUE / 2), |
| -1 + (Long.MAX_VALUE / 2), 0 + (Long.MAX_VALUE / 2), 1 + (Long.MAX_VALUE / 2), |
| }; |
| for (long a : specialValues) { |
| for (long b : specialValues) { |
| BigInteger bdA = BigInteger.valueOf(a); |
| BigInteger bdB = BigInteger.valueOf(b); |
| BigInteger bdSum = bdA.add(bdB); |
| if (bdSum.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0 || |
| bdSum.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) { |
| try { |
| AccurateMath.addExact(a, b); |
| fail("an exception should have been thrown"); |
| } catch (ArithmeticException mae) { |
| // expected |
| } |
| } else { |
| assertEquals(bdSum, BigInteger.valueOf(AccurateMath.addExact(a, b))); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testSubtractExactInt() { |
| int[] specialValues = new int[] { |
| Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 2, |
| Integer.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 2, |
| -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
| -1 - (Integer.MIN_VALUE / 2), 0 - (Integer.MIN_VALUE / 2), 1 - (Integer.MIN_VALUE / 2), |
| -1 + (Integer.MAX_VALUE / 2), 0 + (Integer.MAX_VALUE / 2), 1 + (Integer.MAX_VALUE / 2), |
| }; |
| for (int a : specialValues) { |
| for (int b : specialValues) { |
| BigInteger bdA = BigInteger.valueOf(a); |
| BigInteger bdB = BigInteger.valueOf(b); |
| BigInteger bdSub = bdA.subtract(bdB); |
| if (bdSub.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || |
| bdSub.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { |
| try { |
| AccurateMath.subtractExact(a, b); |
| fail("an exception should have been thrown"); |
| } catch (ArithmeticException mae) { |
| // expected |
| } |
| } else { |
| assertEquals(bdSub, BigInteger.valueOf(AccurateMath.subtractExact(a, b))); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testSubtractExactLong() { |
| long[] specialValues = new long[] { |
| Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 2, |
| Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE - 2, |
| -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
| -1 - (Long.MIN_VALUE / 2), 0 - (Long.MIN_VALUE / 2), 1 - (Long.MIN_VALUE / 2), |
| -1 + (Long.MAX_VALUE / 2), 0 + (Long.MAX_VALUE / 2), 1 + (Long.MAX_VALUE / 2), |
| }; |
| for (long a : specialValues) { |
| for (long b : specialValues) { |
| BigInteger bdA = BigInteger.valueOf(a); |
| BigInteger bdB = BigInteger.valueOf(b); |
| BigInteger bdSub = bdA.subtract(bdB); |
| if (bdSub.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0 || |
| bdSub.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) { |
| try { |
| AccurateMath.subtractExact(a, b); |
| fail("an exception should have been thrown"); |
| } catch (ArithmeticException mae) { |
| // expected |
| } |
| } else { |
| assertEquals(bdSub, BigInteger.valueOf(AccurateMath.subtractExact(a, b))); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testMultiplyExactInt() { |
| int[] specialValues = new int[] { |
| Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 2, |
| Integer.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 2, |
| -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
| -1 - (Integer.MIN_VALUE / 2), 0 - (Integer.MIN_VALUE / 2), 1 - (Integer.MIN_VALUE / 2), |
| -1 + (Integer.MAX_VALUE / 2), 0 + (Integer.MAX_VALUE / 2), 1 + (Integer.MAX_VALUE / 2), |
| }; |
| for (int a : specialValues) { |
| for (int b : specialValues) { |
| BigInteger bdA = BigInteger.valueOf(a); |
| BigInteger bdB = BigInteger.valueOf(b); |
| BigInteger bdMul = bdA.multiply(bdB); |
| if (bdMul.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || |
| bdMul.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { |
| try { |
| AccurateMath.multiplyExact(a, b); |
| fail("an exception should have been thrown " + a + b); |
| } catch (ArithmeticException mae) { |
| // expected |
| } |
| } else { |
| assertEquals(bdMul, BigInteger.valueOf(AccurateMath.multiplyExact(a, b))); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testMultiplyExactLong() { |
| long[] specialValues = new long[] { |
| Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 2, |
| Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE - 2, |
| -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
| -1 - (Long.MIN_VALUE / 2), 0 - (Long.MIN_VALUE / 2), 1 - (Long.MIN_VALUE / 2), |
| -1 + (Long.MAX_VALUE / 2), 0 + (Long.MAX_VALUE / 2), 1 + (Long.MAX_VALUE / 2), |
| }; |
| for (long a : specialValues) { |
| for (long b : specialValues) { |
| BigInteger bdA = BigInteger.valueOf(a); |
| BigInteger bdB = BigInteger.valueOf(b); |
| BigInteger bdMul = bdA.multiply(bdB); |
| if (bdMul.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0 || |
| bdMul.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) { |
| try { |
| AccurateMath.multiplyExact(a, b); |
| fail("an exception should have been thrown " + a + b); |
| } catch (ArithmeticException mae) { |
| // expected |
| } |
| } else { |
| assertEquals(bdMul, BigInteger.valueOf(AccurateMath.multiplyExact(a, b))); |
| } |
| } |
| } |
| } |
| |
| @Test(expected = ArithmeticException.class) |
| public void testToIntExactTooLow() { |
| AccurateMath.toIntExact(-1L + Integer.MIN_VALUE); |
| } |
| |
| @Test(expected = ArithmeticException.class) |
| public void testToIntExactTooHigh() { |
| AccurateMath.toIntExact(+1L + Integer.MAX_VALUE); |
| } |
| |
| @Test |
| public void testToIntExact() { |
| for (int n = -1000; n < 1000; ++n) { |
| assertEquals(n, AccurateMath.toIntExact(0L + n)); |
| } |
| assertEquals(Integer.MIN_VALUE, AccurateMath.toIntExact(0L + Integer.MIN_VALUE)); |
| assertEquals(Integer.MAX_VALUE, AccurateMath.toIntExact(0L + Integer.MAX_VALUE)); |
| } |
| |
| @Test |
| public void testFloorDivInt() { |
| assertEquals(+1, AccurateMath.floorDiv(+4, +3)); |
| assertEquals(-2, AccurateMath.floorDiv(-4, +3)); |
| assertEquals(-2, AccurateMath.floorDiv(+4, -3)); |
| assertEquals(+1, AccurateMath.floorDiv(-4, -3)); |
| try { |
| AccurateMath.floorDiv(1, 0); |
| fail("an exception should have been thrown"); |
| } catch (ArithmeticException mae) { |
| // expected |
| } |
| for (int a = -100; a <= 100; ++a) { |
| for (int b = -100; b <= 100; ++b) { |
| if (b != 0) { |
| assertEquals(poorManFloorDiv(a, b), AccurateMath.floorDiv(a, b)); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testFloorModInt() { |
| assertEquals(+1, AccurateMath.floorMod(+4, +3)); |
| assertEquals(+2, AccurateMath.floorMod(-4, +3)); |
| assertEquals(-2, AccurateMath.floorMod(+4, -3)); |
| assertEquals(-1, AccurateMath.floorMod(-4, -3)); |
| try { |
| AccurateMath.floorMod(1, 0); |
| fail("an exception should have been thrown"); |
| } catch (ArithmeticException mae) { |
| // expected |
| } |
| for (int a = -100; a <= 100; ++a) { |
| for (int b = -100; b <= 100; ++b) { |
| if (b != 0) { |
| assertEquals(poorManFloorMod(a, b), AccurateMath.floorMod(a, b)); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testFloorDivModInt() { |
| UniformRandomProvider rng = RandomSource.WELL_1024_A.create(0x7ccab45edeaab90aL); |
| for (int i = 0; i < 10000; ++i) { |
| int a = rng.nextInt(); |
| int b = rng.nextInt(); |
| if (b == 0) { |
| try { |
| AccurateMath.floorDiv(a, b); |
| fail("an exception should have been thrown"); |
| } catch (ArithmeticException mae) { |
| // expected |
| } |
| } else { |
| int d = AccurateMath.floorDiv(a, b); |
| int m = AccurateMath.floorMod(a, b); |
| assertEquals(AccurateMath.toIntExact(poorManFloorDiv(a, b)), d); |
| assertEquals(AccurateMath.toIntExact(poorManFloorMod(a, b)), m); |
| assertEquals(a, d * b + m); |
| if (b < 0) { |
| assertTrue(m <= 0); |
| assertTrue(-m < -b); |
| } else { |
| assertTrue(m >= 0); |
| assertTrue(m < b); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testFloorDivLong() { |
| assertEquals(+1L, AccurateMath.floorDiv(+4L, +3L)); |
| assertEquals(-2L, AccurateMath.floorDiv(-4L, +3L)); |
| assertEquals(-2L, AccurateMath.floorDiv(+4L, -3L)); |
| assertEquals(+1L, AccurateMath.floorDiv(-4L, -3L)); |
| try { |
| AccurateMath.floorDiv(1L, 0L); |
| fail("an exception should have been thrown"); |
| } catch (ArithmeticException mae) { |
| // expected |
| } |
| for (long a = -100L; a <= 100L; ++a) { |
| for (long b = -100L; b <= 100L; ++b) { |
| if (b != 0) { |
| assertEquals(poorManFloorDiv(a, b), AccurateMath.floorDiv(a, b)); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testFloorModLong() { |
| assertEquals(+1L, AccurateMath.floorMod(+4L, +3L)); |
| assertEquals(+2L, AccurateMath.floorMod(-4L, +3L)); |
| assertEquals(-2L, AccurateMath.floorMod(+4L, -3L)); |
| assertEquals(-1L, AccurateMath.floorMod(-4L, -3L)); |
| try { |
| AccurateMath.floorMod(1L, 0L); |
| fail("an exception should have been thrown"); |
| } catch (ArithmeticException mae) { |
| // expected |
| } |
| for (long a = -100L; a <= 100L; ++a) { |
| for (long b = -100L; b <= 100L; ++b) { |
| if (b != 0) { |
| assertEquals(poorManFloorMod(a, b), AccurateMath.floorMod(a, b)); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testFloorDivModLong() { |
| UniformRandomProvider rng = RandomSource.WELL_1024_A.create(0xb87b9bc14c96ccd5L); |
| for (int i = 0; i < 10000; ++i) { |
| long a = rng.nextLong(); |
| long b = rng.nextLong(); |
| if (b == 0) { |
| try { |
| AccurateMath.floorDiv(a, b); |
| fail("an exception should have been thrown"); |
| } catch (ArithmeticException mae) { |
| // expected |
| } |
| } else { |
| long d = AccurateMath.floorDiv(a, b); |
| long m = AccurateMath.floorMod(a, b); |
| assertEquals(poorManFloorDiv(a, b), d); |
| assertEquals(poorManFloorMod(a, b), m); |
| assertEquals(a, d * b + m); |
| if (b < 0) { |
| assertTrue(m <= 0); |
| assertTrue(-m < -b); |
| } else { |
| assertTrue(m >= 0); |
| assertTrue(m < b); |
| } |
| } |
| } |
| } |
| |
| private long poorManFloorDiv(long a, long b) { |
| |
| // find q0, r0 such that a = q0 b + r0 |
| BigInteger q0 = BigInteger.valueOf(a / b); |
| BigInteger r0 = BigInteger.valueOf(a % b); |
| BigInteger fd = BigInteger.valueOf(Integer.MIN_VALUE); |
| BigInteger bigB = BigInteger.valueOf(b); |
| |
| for (int k = -2; k < 2; ++k) { |
| // find another pair q, r such that a = q b + r |
| BigInteger bigK = BigInteger.valueOf(k); |
| BigInteger q = q0.subtract(bigK); |
| BigInteger r = r0.add(bigK.multiply(bigB)); |
| if (r.abs().compareTo(bigB.abs()) < 0 && |
| (r.longValue() == 0L || ((r.longValue() ^ b) & 0x8000000000000000L) == 0)) { |
| if (fd.compareTo(q) < 0) { |
| fd = q; |
| } |
| } |
| } |
| |
| return fd.longValue(); |
| |
| } |
| |
| private long poorManFloorMod(long a, long b) { |
| return a - b * poorManFloorDiv(a, b); |
| } |
| |
| /** |
| * http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6430675 |
| */ |
| @Test |
| public void testRoundDown() { |
| double x = 0x1.fffffffffffffp-2; // greatest floating point value less than 0.5 |
| assertTrue(x < 0.5d); |
| assertEquals(0, AccurateMath.round(x)); |
| |
| x = 4503599627370497.0; // x = Math.pow(2, 52) + 1; |
| assertEquals("4503599627370497", new BigDecimal(x).toString()); |
| assertEquals(x, Math.rint(x), 0.0); |
| assertEquals(x, AccurateMath.round(x), 0.0); |
| //assertTrue(x == Math.round(x)); // fails with Java 7, fixed in Java 8 |
| } |
| |
| } |