View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  package org.apache.commons.geometry.core.precision;
    18
    19  import org.apache.commons.geometry.core.GeometryTestUtils;
    20  import org.junit.Assert;
    21  import org.junit.Test;
    22
    23  public class EpsilonDoublePrecisionContextTest {
    24
    25      @Test
    26      public void testGetters() {
    27          // arrange
    28          final double eps = 1e-6;
    29
    30          // act
    31          final EpsilonDoublePrecisionContext ctx = new EpsilonDoublePrecisionContext(eps);
    32
    33          // assert
    34          Assert.assertEquals(eps, ctx.getEpsilon(), 0.0);
    35          Assert.assertEquals(eps, ctx.getMaxZero(), 0.0);
    36      }
    37
    38      @Test
    39      public void testInvalidEpsilonValues() {
    40          // act/assert
    41          GeometryTestUtils.assertThrows(() -> {
    42              new EpsilonDoublePrecisionContext(-1.0);
    43          }, IllegalArgumentException.class);
    44
    45          GeometryTestUtils.assertThrows(() -> {
    46              new EpsilonDoublePrecisionContext(Double.NaN);
    47          }, IllegalArgumentException.class, "Invalid epsilon value: NaN");
    48
    49          GeometryTestUtils.assertThrows(() -> {
    50              new EpsilonDoublePrecisionContext(Double.POSITIVE_INFINITY);
    51          }, IllegalArgumentException.class, "Invalid epsilon value: Infinity");
    52
    53          GeometryTestUtils.assertThrows(() -> {
    54              new EpsilonDoublePrecisionContext(Double.NEGATIVE_INFINITY);
    55          }, IllegalArgumentException.class, "Invalid epsilon value: -Infinity");
    56      }
    57
    58      @Test
    59      public void testSign() {
    60          // arrange
    61          final double eps = 1e-2;
    62
    63          final EpsilonDoublePrecisionContext ctx = new EpsilonDoublePrecisionContext(eps);
    64
    65          // act/assert
    66          Assert.assertEquals(0, ctx.sign(0.0));
    67          Assert.assertEquals(0, ctx.sign(-0.0));
    68
    69          Assert.assertEquals(0, ctx.sign(1e-2));
    70          Assert.assertEquals(0, ctx.sign(-1e-2));
    71
    72          Assert.assertEquals(1, ctx.sign(1e-1));
    73          Assert.assertEquals(-1, ctx.sign(-1e-1));
    74
    75          Assert.assertEquals(1, ctx.sign(Double.NaN));
    76          Assert.assertEquals(1, ctx.sign(Double.POSITIVE_INFINITY));
    77          Assert.assertEquals(-1, ctx.sign(Double.NEGATIVE_INFINITY));
    78      }
    79
    80      @Test
    81      public void testCompare_compareToZero() {
    82          // arrange
    83          final double eps = 1e-2;
    84
    85          final EpsilonDoublePrecisionContext ctx = new EpsilonDoublePrecisionContext(eps);
    86
    87          // act/assert
    88          Assert.assertEquals(0, ctx.compare(0.0, 0.0));
    89          Assert.assertEquals(0, ctx.compare(+0.0, -0.0));
    90          Assert.assertEquals(0, ctx.compare(eps, -0.0));
    91          Assert.assertEquals(0, ctx.compare(+0.0, eps));
    92
    93          Assert.assertEquals(0, ctx.compare(-eps, -0.0));
    94          Assert.assertEquals(0, ctx.compare(+0.0, -eps));
    95
    96          Assert.assertEquals(-1, ctx.compare(0.0, 1.0));
    97          Assert.assertEquals(1, ctx.compare(1.0, 0.0));
    98
    99          Assert.assertEquals(1, ctx.compare(0.0, -1.0));
    100         Assert.assertEquals(-1, ctx.compare(-1.0, 0.0));
    101     }
    102
    103     @Test
    104     public void testCompare_compareNonZero() {
    105         // arrange
    106         final double eps = 1e-5;
    107         final double small = 1e-3;
    108         final double big = 1e100;
    109
    110         final EpsilonDoublePrecisionContext ctx = new EpsilonDoublePrecisionContext(eps);
    111
    112         // act/assert
    113         Assert.assertEquals(0, ctx.compare(eps, 2 * eps));
    114         Assert.assertEquals(0, ctx.compare(-2 * eps, -eps));
    115
    116         Assert.assertEquals(0, ctx.compare(small, small + (0.9 * eps)));
    117         Assert.assertEquals(0, ctx.compare(-small - (0.9 * eps), -small));
    118
    119         Assert.assertEquals(0, ctx.compare(big, nextUp(big, 1)));
    120         Assert.assertEquals(0, ctx.compare(nextDown(-big, 1), -big));
    121
    122         Assert.assertEquals(-1, ctx.compare(small, small + (1.1 * eps)));
    123         Assert.assertEquals(1, ctx.compare(-small, -small - (1.1 * eps)));
    124
    125         Assert.assertEquals(-1, ctx.compare(big, nextUp(big, 2)));
    126         Assert.assertEquals(1, ctx.compare(-big, nextDown(-big, 2)));
    127     }
    128
    129     @Test
    130     public void testCompare_NaN() {
    131         // arrange
    132         final EpsilonDoublePrecisionContext ctx = new EpsilonDoublePrecisionContext(1e-6);
    133
    134         // act/assert
    135         Assert.assertEquals(1, ctx.compare(0, Double.NaN));
    136         Assert.assertEquals(1, ctx.compare(Double.NaN, 0));
    137         Assert.assertEquals(1, ctx.compare(Double.NaN, Double.NaN));
    138
    139         Assert.assertEquals(1, ctx.compare(Double.POSITIVE_INFINITY, Double.NaN));
    140         Assert.assertEquals(1, ctx.compare(Double.NaN, Double.POSITIVE_INFINITY));
    141
    142         Assert.assertEquals(1, ctx.compare(Double.NEGATIVE_INFINITY, Double.NaN));
    143         Assert.assertEquals(1, ctx.compare(Double.NaN, Double.NEGATIVE_INFINITY));
    144     }
    145
    146     @Test
    147     public void testCompare_infinity() {
    148         // arrange
    149         final EpsilonDoublePrecisionContext ctx = new EpsilonDoublePrecisionContext(1e-6);
    150
    151         // act/assert
    152         Assert.assertEquals(-1, ctx.compare(0, Double.POSITIVE_INFINITY));
    153         Assert.assertEquals(1, ctx.compare(Double.POSITIVE_INFINITY, 0));
    154         Assert.assertEquals(0, ctx.compare(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
    155
    156         Assert.assertEquals(1, ctx.compare(0, Double.NEGATIVE_INFINITY));
    157         Assert.assertEquals(-1, ctx.compare(Double.NEGATIVE_INFINITY, 0));
    158         Assert.assertEquals(0, ctx.compare(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
    159     }
    160
    161     @Test
    162     public void testGetMaxZero_isZeroEqualityThreshold() {
    163         // arrange
    164         final double eps = 1e-2;
    165
    166         final EpsilonDoublePrecisionContext ctx = new EpsilonDoublePrecisionContext(eps);
    167
    168         final double maxZero = ctx.getMaxZero();
    169
    170         // act/assert
    171         Assert.assertTrue(ctx.eqZero(maxZero));
    172         Assert.assertTrue(ctx.eqZero(nextDown(maxZero, 1)));
    173         Assert.assertFalse(ctx.eqZero(nextUp(maxZero, 1)));
    174
    175         Assert.assertTrue(ctx.eqZero(-maxZero));
    176         Assert.assertTrue(ctx.eqZero(nextUp(-maxZero, 1)));
    177         Assert.assertFalse(ctx.eqZero(nextDown(-maxZero, 1)));
    178     }
    179
    180     @Test
    181     public void testHashCode() {
    182         // arrange
    183         final EpsilonDoublePrecisionContext a = new EpsilonDoublePrecisionContext(1e-6);
    184         final EpsilonDoublePrecisionContext b = new EpsilonDoublePrecisionContext(1e-7);
    185         final EpsilonDoublePrecisionContext c = new EpsilonDoublePrecisionContext(1e-6);
    186
    187         // act/assert
    188         Assert.assertEquals(a.hashCode(), a.hashCode());
    189         Assert.assertEquals(a.hashCode(), c.hashCode());
    190
    191         Assert.assertNotEquals(a.hashCode(), b.hashCode());
    192     }
    193
    194     @Test
    195     public void testEquals() {
    196         // arrange
    197         final EpsilonDoublePrecisionContext a = new EpsilonDoublePrecisionContext(1e-6);
    198         final EpsilonDoublePrecisionContext b = new EpsilonDoublePrecisionContext(1e-7);
    199         final EpsilonDoublePrecisionContext c = new EpsilonDoublePrecisionContext(1e-6);
    200
    201         // act/assert
    202         Assert.assertFalse(a.equals(null));
    203         Assert.assertFalse(a.equals(new Object()));
    204         Assert.assertNotEquals(a, b);
    205         Assert.assertNotEquals(b, a);
    206
    207         Assert.assertEquals(a, a);
    208         Assert.assertEquals(a, c);
    209     }
    210
    211     @Test
    212     public void testEqualsAndHashCode_signedZeroConsistency() {
    213         // arrange
    214         final EpsilonDoublePrecisionContext a = new EpsilonDoublePrecisionContext(0.0);
    215         final EpsilonDoublePrecisionContext b = new EpsilonDoublePrecisionContext(-0.0);
    216         final EpsilonDoublePrecisionContext c = new EpsilonDoublePrecisionContext(0.0);
    217         final EpsilonDoublePrecisionContext d = new EpsilonDoublePrecisionContext(-0.0);
    218
    219         // act/assert
    220         Assert.assertFalse(a.equals(b));
    221         Assert.assertNotEquals(a.hashCode(), b.hashCode());
    222
    223         Assert.assertTrue(a.equals(c));
    224         Assert.assertEquals(a.hashCode(), c.hashCode());
    225
    226         Assert.assertTrue(b.equals(d));
    227         Assert.assertEquals(b.hashCode(), d.hashCode());
    228     }
    229
    230     @Test
    231     public void testToString() {
    232         // arrange
    233         final EpsilonDoublePrecisionContext a = new EpsilonDoublePrecisionContext(1d);
    234
    235         // act
    236         final String str = a.toString();
    237
    238         // assert
    239         Assert.assertTrue(str.contains("EpsilonDoublePrecisionContext"));
    240         Assert.assertTrue(str.contains("epsilon= 1"));
    241     }
    242
    243     /**
    244      * Increments the given double value {@code count} number of times
    245      * using {@link Math#nextUp(double)}.
    246      * @param n
    247      * @param count
    248      * @return
    249      */
    250     private static double nextUp(final double n, final int count) {
    251         double result = n;
    252         for (int i = 0; i < count; ++i) {
    253             result = Math.nextUp(result);
    254         }
    255
    256         return result;
    257     }
    258
    259     /**
    260      * Decrements the given double value {@code count} number of times
    261      * using {@link Math#nextDown(double)}.
    262      * @param n
    263      * @param count
    264      * @return
    265      */
    266     private static double nextDown(final double n, final int count) {
    267         double result = n;
    268         for (int i = 0; i < count; ++i) {
    269             result = Math.nextDown(result);
    270         }
    271
    272         return result;
    273     }
    274 }