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 <matt.juntu...@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;