Repository: commons-complex Updated Branches: refs/heads/master 1aba3c8ca -> 7524db548
[COMPLEX-1] Fix equals methods in Complex and Quaternion by copying over Precision from commons-math. Made Precision package protected and removed parts not used by code currently in commons-complex. This fixes ComplexTest and QuaternionTest unit tests. Fix RootsOfUnityTest by changing test methods to expect the correct exception class. Project: http://git-wip-us.apache.org/repos/asf/commons-complex/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-complex/commit/f7cca80f Tree: http://git-wip-us.apache.org/repos/asf/commons-complex/tree/f7cca80f Diff: http://git-wip-us.apache.org/repos/asf/commons-complex/diff/f7cca80f Branch: refs/heads/master Commit: f7cca80f4a6913f22829ad5dfcebd980ce5289cd Parents: 1aba3c8 Author: Ray DeCampo <[email protected]> Authored: Fri Jan 6 17:35:51 2017 -0500 Committer: Ray DeCampo <[email protected]> Committed: Fri Jan 6 17:35:51 2017 -0500 ---------------------------------------------------------------------- .../org/apache/commons/complex/Complex.java | 12 +- .../org/apache/commons/complex/Precision.java | 128 +++++++++++++++++++ .../org/apache/commons/complex/Quaternion.java | 27 +--- .../commons/complex/RootsOfUnityTest.java | 13 +- 4 files changed, 145 insertions(+), 35 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-complex/blob/f7cca80f/src/main/java/org/apache/commons/complex/Complex.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/complex/Complex.java b/src/main/java/org/apache/commons/complex/Complex.java index 8f289e2..937820d 100644 --- a/src/main/java/org/apache/commons/complex/Complex.java +++ b/src/main/java/org/apache/commons/complex/Complex.java @@ -363,8 +363,8 @@ public class Complex implements Serializable { * @since 3.3 */ public static boolean equals(Complex x, Complex y, int maxUlps) { - return equals(x.real, y.real) && - equals(x.imaginary, y.imaginary); + return Precision.equals(x.real, y.real, maxUlps) && + Precision.equals(x.imaginary, y.imaginary, maxUlps); } /** @@ -397,8 +397,8 @@ public class Complex implements Serializable { * @since 3.3 */ public static boolean equals(Complex x, Complex y, double eps) { - return equals(x.real, y.real) && - equals(x.imaginary, y.imaginary); + return Precision.equals(x.real, y.real, eps) && + Precision.equals(x.imaginary, y.imaginary, eps); } /** @@ -418,8 +418,8 @@ public class Complex implements Serializable { */ public static boolean equalsWithRelativeTolerance(Complex x, Complex y, double eps) { - return equalsWithRelativeTolerance(x.real, y.real, eps) && - equalsWithRelativeTolerance(x.imaginary, y.imaginary, eps); + return Precision.equalsWithRelativeTolerance(x.real, y.real, eps) && + Precision.equalsWithRelativeTolerance(x.imaginary, y.imaginary, eps); } /** http://git-wip-us.apache.org/repos/asf/commons-complex/blob/f7cca80f/src/main/java/org/apache/commons/complex/Precision.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/complex/Precision.java b/src/main/java/org/apache/commons/complex/Precision.java new file mode 100644 index 0000000..77f5048 --- /dev/null +++ b/src/main/java/org/apache/commons/complex/Precision.java @@ -0,0 +1,128 @@ +/* + * 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.complex; + +/** + * Utilities for comparing numbers. + */ +class Precision { + /** Offset to order signed double numbers lexicographically. */ + private static final long SGN_MASK = 0x8000000000000000L; + /** Positive zero bits. */ + private static final long POSITIVE_ZERO_DOUBLE_BITS = Double.doubleToRawLongBits(+0.0); + /** Negative zero bits. */ + private static final long NEGATIVE_ZERO_DOUBLE_BITS = Double.doubleToRawLongBits(-0.0); + + /** + * Private constructor. + */ + private Precision() {} + + /** + * Returns {@code true} if there is no double value strictly between the + * arguments or the difference between them is within the range of allowed + * error (inclusive). Returns {@code false} if either of the arguments + * is NaN. + * + * @param x First value. + * @param y Second value. + * @param eps Amount of allowed absolute error. + * @return {@code true} if the values are two adjacent floating point + * numbers or they are within range of each other. + */ + public static boolean equals(double x, double y, double eps) { + return equals(x, y, 1) || Math.abs(y - x) <= eps; + } + + /** + * Returns {@code true} if there is no double value strictly between the + * arguments or the relative difference between them is less than or equal + * to the given tolerance. Returns {@code false} if either of the arguments + * is NaN. + * + * @param x First value. + * @param y Second value. + * @param eps Amount of allowed relative error. + * @return {@code true} if the values are two adjacent floating point + * numbers or they are within range of each other. + */ + public static boolean equalsWithRelativeTolerance(double x, double y, double eps) { + if (equals(x, y, 1)) { + return true; + } + + final double absoluteMax = Math.max(Math.abs(x), Math.abs(y)); + final double relativeDifference = Math.abs((x - y) / absoluteMax); + + return relativeDifference <= eps; + } + + /** + * Returns true if the arguments are equal or within the range of allowed + * error (inclusive). + * <p> + * Two float numbers are considered equal if there are {@code (maxUlps - 1)} + * (or fewer) floating point numbers between them, i.e. two adjacent + * floating point numbers are considered equal. + * </p> + * <p> + * Adapted from <a + * href="http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/"> + * Bruce Dawson</a>. Returns {@code false} if either of the arguments is NaN. + * </p> + * + * @param x first value + * @param y second value + * @param maxUlps {@code (maxUlps - 1)} is the number of floating point + * values between {@code x} and {@code y}. + * @return {@code true} if there are fewer than {@code maxUlps} floating + * point values between {@code x} and {@code y}. + */ + public static boolean equals(final double x, final double y, final int maxUlps) { + + final long xInt = Double.doubleToRawLongBits(x); + final long yInt = Double.doubleToRawLongBits(y); + + final boolean isEqual; + if (((xInt ^ yInt) & SGN_MASK) == 0l) { + // number have same sign, there is no risk of overflow + isEqual = Math.abs(xInt - yInt) <= maxUlps; + } else { + // number have opposite signs, take care of overflow + final long deltaPlus; + final long deltaMinus; + if (xInt < yInt) { + deltaPlus = yInt - POSITIVE_ZERO_DOUBLE_BITS; + deltaMinus = xInt - NEGATIVE_ZERO_DOUBLE_BITS; + } else { + deltaPlus = xInt - POSITIVE_ZERO_DOUBLE_BITS; + deltaMinus = yInt - NEGATIVE_ZERO_DOUBLE_BITS; + } + + if (deltaPlus > maxUlps) { + isEqual = false; + } else { + isEqual = deltaMinus <= (maxUlps - deltaPlus); + } + + } + + return isEqual && !Double.isNaN(x) && !Double.isNaN(y); + + } +} http://git-wip-us.apache.org/repos/asf/commons-complex/blob/f7cca80f/src/main/java/org/apache/commons/complex/Quaternion.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/complex/Quaternion.java b/src/main/java/org/apache/commons/complex/Quaternion.java index 7ce8a30..6fc29a3 100644 --- a/src/main/java/org/apache/commons/complex/Quaternion.java +++ b/src/main/java/org/apache/commons/complex/Quaternion.java @@ -306,10 +306,10 @@ public final class Quaternion implements Serializable { */ public boolean equals(final Quaternion q, final double eps) { - return equals(q0, q.getQ0(), eps) && - equals(q1, q.getQ1(), eps) && - equals(q2, q.getQ2(), eps) && - equals(q3, q.getQ3(), eps); + return Precision.equals(q0, q.getQ0(), eps) && + Precision.equals(q1, q.getQ1(), eps) && + Precision.equals(q2, q.getQ2(), eps) && + Precision.equals(q3, q.getQ3(), eps); } /** @@ -321,7 +321,7 @@ public final class Quaternion implements Serializable { * {@code false} otherwise */ public boolean isUnitQuaternion(double eps) { - return equals(getNorm(), 1d, eps); + return Precision.equals(getNorm(), 1d, eps); } /** @@ -464,21 +464,4 @@ public final class Quaternion implements Serializable { return s.toString(); } - /** - * Returns {@code true} if there is no double value strictly between the - * arguments or the difference between them is within the range of allowed - * error (inclusive). Returns {@code false} if either of the arguments - * is NaN. - * - * @param x First value. - * @param y Second value. - * @param eps Amount of allowed absolute error. - * @return {@code true} if the values are two adjacent floating point - * numbers or they are within range of each other. - */ - private static boolean equals(double x, double y, double eps) { - return equals(x, y, 1) || Math.abs(y - x) <= eps; - } - - } http://git-wip-us.apache.org/repos/asf/commons-complex/blob/f7cca80f/src/test/java/org/apache/commons/complex/RootsOfUnityTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/complex/RootsOfUnityTest.java b/src/test/java/org/apache/commons/complex/RootsOfUnityTest.java index 1929c21..81ab1a3 100644 --- a/src/test/java/org/apache/commons/complex/RootsOfUnityTest.java +++ b/src/test/java/org/apache/commons/complex/RootsOfUnityTest.java @@ -16,7 +16,6 @@ */ package org.apache.commons.complex; -import org.apache.commons.complex.RootsOfUnity; import org.junit.Assert; import org.junit.Test; @@ -27,20 +26,20 @@ import org.junit.Test; */ public class RootsOfUnityTest { - @Test(expected = IllegalStateException.class) - public void testMathIllegalState1() { + @Test(expected = IllegalArgumentException.class) + public void testMathIllegalArgument1() { final RootsOfUnity roots = new RootsOfUnity(); roots.getReal(0); } - @Test(expected = IllegalStateException.class) - public void testMathIllegalState2() { + @Test(expected = IllegalArgumentException.class) + public void testMathIllegalArgument2() { final RootsOfUnity roots = new RootsOfUnity(); roots.getImaginary(0); } - @Test(expected = IllegalStateException.class) - public void testMathIllegalState3() { + @Test(expected = IllegalArgumentException.class) + public void testMathIllegalArgument3() { final RootsOfUnity roots = new RootsOfUnity(); roots.isCounterClockWise(); }
