You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by er...@apache.org on 2019/02/14 21:36:51 UTC

[commons-numbers] 01/03: NUMBERS-94: adding extra check to PlaneAngle.normalize() to handle very small numbers

This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-numbers.git

commit 00b2b5621ed39141d81b92b3fa3ba96a4e54c1c1
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Thu Feb 14 00:12:51 2019 -0500

    NUMBERS-94: adding extra check to PlaneAngle.normalize() to handle very small numbers
---
 .../apache/commons/numbers/angle/PlaneAngle.java   | 16 +++++++++++++++-
 .../commons/numbers/angle/PlaneAngleTest.java      | 22 ++++++++++++++++++++++
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/commons-numbers-angle/src/main/java/org/apache/commons/numbers/angle/PlaneAngle.java b/commons-numbers-angle/src/main/java/org/apache/commons/numbers/angle/PlaneAngle.java
index cf8f509..8a7ce0e 100644
--- a/commons-numbers-angle/src/main/java/org/apache/commons/numbers/angle/PlaneAngle.java
+++ b/commons-numbers-angle/src/main/java/org/apache/commons/numbers/angle/PlaneAngle.java
@@ -94,7 +94,21 @@ public class PlaneAngle {
      * {@code center - 0.5 <= a - k < center + 0.5} (in turns).
      */
     public PlaneAngle normalize(PlaneAngle center) {
-        return new PlaneAngle(value - Math.floor(value + HALF_TURN - center.value));
+        final double lowerBound = center.value - HALF_TURN;
+        final double upperBound = center.value + HALF_TURN;
+
+        double normalized = value - Math.floor(value - lowerBound);
+
+        // If value is too small to be representable compared to the floor
+        // expression above (ie, if value + x = x), then we may end up with a number
+        // exactly equal to the upper bound here. In that case, subtract
+        // one from the normalized value so that we can fulfill the contract
+        // of only returning results strictly less than the upper bound.
+        if (normalized >= upperBound) {
+            normalized -= 1.0;
+        }
+
+        return new PlaneAngle(normalized);
     }
 
     /**
diff --git a/commons-numbers-angle/src/test/java/org/apache/commons/numbers/angle/PlaneAngleTest.java b/commons-numbers-angle/src/test/java/org/apache/commons/numbers/angle/PlaneAngleTest.java
index 3e7164e..d2f3ac8 100644
--- a/commons-numbers-angle/src/test/java/org/apache/commons/numbers/angle/PlaneAngleTest.java
+++ b/commons-numbers-angle/src/test/java/org/apache/commons/numbers/angle/PlaneAngleTest.java
@@ -126,6 +126,28 @@ public class PlaneAngleTest {
     }
 
     @Test
+    public void testNormalizeVeryCloseToBounds() {
+        // arrange
+        double eps = 1e-22;
+
+        double small = 1e-16;
+        double tiny = 1e-18; // 0.5 + tiny = 0.5 (the value is too small to add to 0.5)
+
+        // act/assert
+        Assert.assertEquals(1.0 - small, PlaneAngle.ofTurns(-small).normalize(PlaneAngle.PI).toTurns(), eps);
+        Assert.assertEquals(small, PlaneAngle.ofTurns(small).normalize(PlaneAngle.PI).toTurns(), eps);
+
+        Assert.assertEquals(0.5 - small, PlaneAngle.ofTurns(-0.5 - small).normalize(PlaneAngle.ZERO).toTurns(), eps);
+        Assert.assertEquals(-0.5 + small, PlaneAngle.ofTurns(0.5 + small).normalize(PlaneAngle.ZERO).toTurns(), eps);
+
+        Assert.assertEquals(0.0, PlaneAngle.ofTurns(-tiny).normalize(PlaneAngle.PI).toTurns(), eps);
+        Assert.assertEquals(tiny, PlaneAngle.ofTurns(tiny).normalize(PlaneAngle.PI).toTurns(), eps);
+
+        Assert.assertEquals(-0.5, PlaneAngle.ofTurns(-0.5 - tiny).normalize(PlaneAngle.ZERO).toTurns(), eps);
+        Assert.assertEquals(-0.5, PlaneAngle.ofTurns(0.5 + tiny).normalize(PlaneAngle.ZERO).toTurns(), eps);
+    }
+
+    @Test
     public void testHashCode() {
         // Test assumes that the internal representation is in "turns".
         final double value = -123.456789;