common: Add saturated arithmetic for int values
authorPatrick Tasse <patrick.tasse@gmail.com>
Tue, 23 Aug 2016 20:26:13 +0000 (16:26 -0400)
committerPatrick Tasse <patrick.tasse@gmail.com>
Wed, 24 Aug 2016 15:05:10 +0000 (11:05 -0400)
Change-Id: If59537b402015ab41db3ffe92b5a2171840f26bd
Signed-off-by: Patrick Tasse <patrick.tasse@gmail.com>
Reviewed-on: https://git.eclipse.org/r/79564
Reviewed-by: Hudson CI
Reviewed-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Tested-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
common/org.eclipse.tracecompass.common.core.tests/src/org/eclipse/tracecompass/common/core/tests/math/SaturatedArithmeticTest.java
common/org.eclipse.tracecompass.common.core/src/org/eclipse/tracecompass/common/core/math/SaturatedArithmetic.java

index b6db807b3cd32180c9de6764a19cb5f3a48d5b25..57fd5ab74a3432cd516375fa3d292e069843682f 100644 (file)
@@ -12,21 +12,24 @@ package org.eclipse.tracecompass.common.core.tests.math;
 import static org.eclipse.tracecompass.common.core.math.SaturatedArithmetic.add;
 import static org.eclipse.tracecompass.common.core.math.SaturatedArithmetic.multiply;
 import static org.eclipse.tracecompass.common.core.math.SaturatedArithmetic.sameSign;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import org.eclipse.tracecompass.common.core.math.SaturatedArithmetic;
 import org.junit.Test;
 
 /**
- * Test suite for the {@link SaturatedArithmetic} All tests must test
- * reciprocity as well as low and high limits
+ * Test suite for the {@link SaturatedArithmetic} class.
+ * <p>
+ * All tests must test reciprocity as well as low and high limits.
  *
  * @author Matthew Khouzam
  */
 public class SaturatedArithmeticTest {
 
     /**
-     * test absorbtion
+     * test multiplication absorption (int)
      */
     @Test
     public void testMult0() {
@@ -37,14 +40,14 @@ public class SaturatedArithmeticTest {
         assertEquals(0, multiply(0, 42));
         assertEquals(0, multiply(-42, 0));
         assertEquals(0, multiply(0, -42));
-        assertEquals(0, multiply(Long.MAX_VALUE, 0));
-        assertEquals(0, multiply(0, Long.MAX_VALUE));
-        assertEquals(0, multiply(Long.MIN_VALUE, 0));
-        assertEquals(0, multiply(0, Long.MIN_VALUE));
+        assertEquals(0, multiply(Integer.MAX_VALUE, 0));
+        assertEquals(0, multiply(0, Integer.MAX_VALUE));
+        assertEquals(0, multiply(Integer.MIN_VALUE, 0));
+        assertEquals(0, multiply(0, Integer.MIN_VALUE));
     }
 
     /**
-     * test identity
+     * test multiplication identity (int)
      */
     @Test
     public void testMult1() {
@@ -54,14 +57,14 @@ public class SaturatedArithmeticTest {
         assertEquals(42, multiply(1, 42));
         assertEquals(-42, multiply(-42, 1));
         assertEquals(-42, multiply(1, -42));
-        assertEquals(Long.MAX_VALUE, multiply(Long.MAX_VALUE, 1));
-        assertEquals(Long.MAX_VALUE, multiply(1, Long.MAX_VALUE));
-        assertEquals(Long.MIN_VALUE, multiply(Long.MIN_VALUE, 1));
-        assertEquals(Long.MIN_VALUE, multiply(1, Long.MIN_VALUE));
+        assertEquals(Integer.MAX_VALUE, multiply(Integer.MAX_VALUE, 1));
+        assertEquals(Integer.MAX_VALUE, multiply(1, Integer.MAX_VALUE));
+        assertEquals(Integer.MIN_VALUE, multiply(Integer.MIN_VALUE, 1));
+        assertEquals(Integer.MIN_VALUE, multiply(1, Integer.MIN_VALUE));
     }
 
     /**
-     * test typical
+     * test multiplication typical (int)
      */
     @Test
     public void testMult100() {
@@ -70,22 +73,89 @@ public class SaturatedArithmeticTest {
         assertEquals(-10000, multiply(-100, 100));
         assertEquals(10000, multiply(-100, -100));
 
-        assertEquals(Long.MAX_VALUE, multiply(Long.MAX_VALUE, 100));
-        assertEquals(Long.MAX_VALUE, multiply(100, Long.MAX_VALUE));
-        assertEquals(Long.MIN_VALUE, multiply(Long.MIN_VALUE, 100));
-        assertEquals(Long.MIN_VALUE, multiply(100, Long.MIN_VALUE));
+        assertEquals(Integer.MAX_VALUE, multiply(Integer.MAX_VALUE, 100));
+        assertEquals(Integer.MAX_VALUE, multiply(100, Integer.MAX_VALUE));
+        assertEquals(Integer.MIN_VALUE, multiply(Integer.MIN_VALUE, 100));
+        assertEquals(Integer.MIN_VALUE, multiply(100, Integer.MIN_VALUE));
 
-        assertEquals(Long.MIN_VALUE, multiply(Long.MAX_VALUE, -100));
-        assertEquals(Long.MIN_VALUE, multiply(-100, Long.MAX_VALUE));
-        assertEquals(Long.MAX_VALUE, multiply(Long.MIN_VALUE, -100));
-        assertEquals(Long.MAX_VALUE, multiply(-100, Long.MIN_VALUE));
+        assertEquals(Integer.MIN_VALUE, multiply(Integer.MAX_VALUE, -100));
+        assertEquals(Integer.MIN_VALUE, multiply(-100, Integer.MAX_VALUE));
+        assertEquals(Integer.MAX_VALUE, multiply(Integer.MIN_VALUE, -100));
+        assertEquals(Integer.MAX_VALUE, multiply(-100, Integer.MIN_VALUE));
     }
 
     /**
-     * test limit
+     * test multiplication limit (int)
      */
     @Test
     public void testMultLimit() {
+        assertEquals(Integer.MAX_VALUE, multiply(Integer.MAX_VALUE, Integer.MAX_VALUE));
+        assertEquals(Integer.MIN_VALUE, multiply(Integer.MAX_VALUE, Integer.MIN_VALUE));
+        assertEquals(Integer.MIN_VALUE, multiply(Integer.MIN_VALUE, Integer.MAX_VALUE));
+        assertEquals(Integer.MAX_VALUE, multiply(Integer.MIN_VALUE, Integer.MIN_VALUE));
+    }
+
+    /**
+     * test multiplication absorption (long)
+     */
+    @Test
+    public void testMult0L() {
+        assertEquals(0, multiply(0L, 0L));
+        assertEquals(0, multiply(0L, 1L));
+        assertEquals(0, multiply(1L, 0L));
+        assertEquals(0, multiply(42L, 0L));
+        assertEquals(0, multiply(0L, 42L));
+        assertEquals(0, multiply(-42L, 0L));
+        assertEquals(0, multiply(0L, -42L));
+        assertEquals(0, multiply(Long.MAX_VALUE, 0L));
+        assertEquals(0, multiply(0L, Long.MAX_VALUE));
+        assertEquals(0, multiply(Long.MIN_VALUE, 0L));
+        assertEquals(0, multiply(0L, Long.MIN_VALUE));
+    }
+
+    /**
+     * test multiplication identity (long)
+     */
+    @Test
+    public void testMult1L() {
+        assertEquals(0, multiply(0L, 1L));
+        assertEquals(1, multiply(1L, 1L));
+        assertEquals(42, multiply(42L, 1L));
+        assertEquals(42, multiply(1L, 42L));
+        assertEquals(-42, multiply(-42L, 1L));
+        assertEquals(-42, multiply(1L, -42L));
+        assertEquals(Long.MAX_VALUE, multiply(Long.MAX_VALUE, 1L));
+        assertEquals(Long.MAX_VALUE, multiply(1L, Long.MAX_VALUE));
+        assertEquals(Long.MIN_VALUE, multiply(Long.MIN_VALUE, 1L));
+        assertEquals(Long.MIN_VALUE, multiply(1L, Long.MIN_VALUE));
+    }
+
+    /**
+     * test multiplication typical (long)
+     */
+    @Test
+    public void testMult100L() {
+        assertEquals(10000, multiply(100L, 100L));
+        assertEquals(-10000, multiply(100L, -100L));
+        assertEquals(-10000, multiply(-100L, 100L));
+        assertEquals(10000, multiply(-100L, -100L));
+
+        assertEquals(Long.MAX_VALUE, multiply(Long.MAX_VALUE, 100L));
+        assertEquals(Long.MAX_VALUE, multiply(100L, Long.MAX_VALUE));
+        assertEquals(Long.MIN_VALUE, multiply(Long.MIN_VALUE, 100L));
+        assertEquals(Long.MIN_VALUE, multiply(100L, Long.MIN_VALUE));
+
+        assertEquals(Long.MIN_VALUE, multiply(Long.MAX_VALUE, -100L));
+        assertEquals(Long.MIN_VALUE, multiply(-100L, Long.MAX_VALUE));
+        assertEquals(Long.MAX_VALUE, multiply(Long.MIN_VALUE, -100L));
+        assertEquals(Long.MAX_VALUE, multiply(-100L, Long.MIN_VALUE));
+    }
+
+    /**
+     * test multiplication limit (long)
+     */
+    @Test
+    public void testMultLimitL() {
         assertEquals(Long.MAX_VALUE, multiply(Long.MAX_VALUE, Long.MAX_VALUE));
         assertEquals(Long.MIN_VALUE, multiply(Long.MAX_VALUE, Long.MIN_VALUE));
         assertEquals(Long.MIN_VALUE, multiply(Long.MIN_VALUE, Long.MAX_VALUE));
@@ -93,7 +163,7 @@ public class SaturatedArithmeticTest {
     }
 
     /**
-     * test identity
+     * test addition identity (int)
      */
     @Test
     public void testAdd0() {
@@ -106,14 +176,14 @@ public class SaturatedArithmeticTest {
         assertEquals(-42, add(-42, 0));
         assertEquals(-42, add(0, -42));
 
-        assertEquals(Long.MAX_VALUE, add(Long.MAX_VALUE, 0));
-        assertEquals(Long.MAX_VALUE, add(0, Long.MAX_VALUE));
-        assertEquals(Long.MIN_VALUE, add(Long.MIN_VALUE, 0));
-        assertEquals(Long.MIN_VALUE, add(0, Long.MIN_VALUE));
+        assertEquals(Integer.MAX_VALUE, add(Integer.MAX_VALUE, 0));
+        assertEquals(Integer.MAX_VALUE, add(0, Integer.MAX_VALUE));
+        assertEquals(Integer.MIN_VALUE, add(Integer.MIN_VALUE, 0));
+        assertEquals(Integer.MIN_VALUE, add(0, Integer.MIN_VALUE));
     }
 
     /**
-     * test typical
+     * test addition typical (int)
      */
     @Test
     public void testAdd100() {
@@ -122,26 +192,78 @@ public class SaturatedArithmeticTest {
         assertEquals(0, add(-100, 100));
         assertEquals(-200, add(-100, -100));
 
-        assertEquals(Long.MAX_VALUE, add(Long.MAX_VALUE, 100));
-        assertEquals(Long.MAX_VALUE, add(100, Long.MAX_VALUE));
-        assertEquals(Long.MIN_VALUE + 100, add(Long.MIN_VALUE, 100));
-        assertEquals(Long.MIN_VALUE + 100, add(100, Long.MIN_VALUE));
+        assertEquals(Integer.MAX_VALUE, add(Integer.MAX_VALUE, 100));
+        assertEquals(Integer.MAX_VALUE, add(100, Integer.MAX_VALUE));
+        assertEquals(Integer.MIN_VALUE + 100, add(Integer.MIN_VALUE, 100));
+        assertEquals(Integer.MIN_VALUE + 100, add(100, Integer.MIN_VALUE));
 
-        assertEquals(Long.MAX_VALUE - 100, add(Long.MAX_VALUE, -100));
-        assertEquals(Long.MAX_VALUE - 100, add(-100, Long.MAX_VALUE));
-        assertEquals(Long.MIN_VALUE, add(Long.MIN_VALUE, -100));
-        assertEquals(Long.MIN_VALUE, add(-100, Long.MIN_VALUE));
+        assertEquals(Integer.MAX_VALUE - 100, add(Integer.MAX_VALUE, -100));
+        assertEquals(Integer.MAX_VALUE - 100, add(-100, Integer.MAX_VALUE));
+        assertEquals(Integer.MIN_VALUE, add(Integer.MIN_VALUE, -100));
+        assertEquals(Integer.MIN_VALUE, add(-100, Integer.MIN_VALUE));
     }
 
     /**
-     * test limit
+     * test addition limit (int)
      */
     @Test
     public void testAddLimit() {
+        assertEquals(Integer.MAX_VALUE, add(Integer.MAX_VALUE, Integer.MAX_VALUE));
+        // min value is 1 larger than max value
+        assertEquals(-1, add(Integer.MAX_VALUE, Integer.MIN_VALUE));
+        assertEquals(-1, add(Integer.MIN_VALUE, Integer.MAX_VALUE));
+        assertEquals(Integer.MIN_VALUE, add(Integer.MIN_VALUE, Integer.MIN_VALUE));
+    }
+
+    /**
+     * test addition identity (long)
+     */
+    @Test
+    public void testAdd0L() {
+        assertEquals(0, add(0L, 0L));
+        assertEquals(1, add(0L, 1L));
+        assertEquals(1, add(1L, 0L));
+
+        assertEquals(42, add(42L, 0L));
+        assertEquals(42, add(0L, 42L));
+        assertEquals(-42, add(-42L, 0L));
+        assertEquals(-42, add(0L, -42L));
+
+        assertEquals(Long.MAX_VALUE, add(Long.MAX_VALUE, 0L));
+        assertEquals(Long.MAX_VALUE, add(0L, Long.MAX_VALUE));
+        assertEquals(Long.MIN_VALUE, add(Long.MIN_VALUE, 0L));
+        assertEquals(Long.MIN_VALUE, add(0L, Long.MIN_VALUE));
+    }
+
+    /**
+     * test addition typical (long)
+     */
+    @Test
+    public void testAdd100L() {
+        assertEquals(200, add(100L, 100L));
+        assertEquals(0, add(100L, -100L));
+        assertEquals(0, add(-100L, 100L));
+        assertEquals(-200, add(-100L, -100L));
+
+        assertEquals(Long.MAX_VALUE, add(Long.MAX_VALUE, 100L));
+        assertEquals(Long.MAX_VALUE, add(100L, Long.MAX_VALUE));
+        assertEquals(Long.MIN_VALUE + 100, add(Long.MIN_VALUE, 100L));
+        assertEquals(Long.MIN_VALUE + 100, add(100L, Long.MIN_VALUE));
+
+        assertEquals(Long.MAX_VALUE - 100, add(Long.MAX_VALUE, -100L));
+        assertEquals(Long.MAX_VALUE - 100, add(-100L, Long.MAX_VALUE));
+        assertEquals(Long.MIN_VALUE, add(Long.MIN_VALUE, -100L));
+        assertEquals(Long.MIN_VALUE, add(-100L, Long.MIN_VALUE));
+    }
+
+    /**
+     * test addition limit (long)
+     */
+    @Test
+    public void testAddLimitL() {
         assertEquals(Long.MAX_VALUE, add(Long.MAX_VALUE, Long.MAX_VALUE));
-        assertEquals(-1, add(Long.MAX_VALUE, Long.MIN_VALUE)); // min value is 1
-                                                               // larger than
-                                                               // max value
+        // min value is 1 larger than max value
+        assertEquals(-1, add(Long.MAX_VALUE, Long.MIN_VALUE));
         assertEquals(-1, add(Long.MIN_VALUE, Long.MAX_VALUE));
         assertEquals(Long.MIN_VALUE, add(Long.MIN_VALUE, Long.MIN_VALUE));
     }
index 9226d36b1309b8271e3850d909b2b9c92d6acf87..a57533f00361823c62caeabce2b9a642bb5d4729 100644 (file)
@@ -22,6 +22,27 @@ public final class SaturatedArithmetic {
         // do nothing
     }
 
+    /**
+     * Saturated multiplication. It will not overflow but instead clamp the
+     * result to {@link Integer#MAX_VALUE} and {@link Integer#MIN_VALUE}.
+     *
+     * @param left
+     *            The left int to multiply
+     * @param right
+     *            The right int to multiply
+     * @return The saturated multiplication result. The mathematical, not Java
+     *         version of Min(Max(MIN_VALUE, left*right), MAX_VALUE).
+     * @see <a href="http://en.wikipedia.org/wiki/Saturation_arithmetic">
+     *      Saturation arithmetic</a>
+     */
+    public static int multiply(int left, int right) {
+        int retVal = left * right;
+        if ((left != 0) && ((retVal / left) != right)) {
+            return (sameSign(left, right) ? Integer.MAX_VALUE : Integer.MIN_VALUE);
+        }
+        return retVal;
+    }
+
     /**
      * Saturated multiplication. It will not overflow but instead clamp the
      * result to {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}.
@@ -43,6 +64,30 @@ public final class SaturatedArithmetic {
         return retVal;
     }
 
+    /**
+     * Saturated addition. It will not overflow but instead clamp the result to
+     * {@link Integer#MAX_VALUE} and {@link Integer#MIN_VALUE}.
+     *
+     * @param left
+     *            The left int to add
+     * @param right
+     *            The right int to add
+     * @return The saturated addition result. The mathematical, not Java version
+     *         of Min(Max(MIN_VALUE, left+right), MAX_VALUE).
+     * @see <a href="http://en.wikipedia.org/wiki/Saturation_arithmetic">
+     *      Saturation arithmetic</a>
+     */
+    public static final int add(final int left, final int right) {
+        int retVal = left + right;
+        if (sameSign(left, right) && !sameSign(left, retVal)) {
+            if (retVal > 0 || left == Integer.MIN_VALUE) {
+                return Integer.MIN_VALUE;
+            }
+            return Integer.MAX_VALUE;
+        }
+        return retVal;
+    }
+
     /**
      * Saturated addition. It will not overflow but instead clamp the result to
      * {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}.
@@ -55,7 +100,6 @@ public final class SaturatedArithmetic {
      *         of Min(Max(MIN_VALUE, left+right), MAX_VALUE).
      * @see <a href="http://en.wikipedia.org/wiki/Saturation_arithmetic">
      *      Saturation arithmetic</a>
-     * @since 2.0
      */
     public static final long add(final long left, final long right) {
         long retVal = left + right;
This page took 0.030457 seconds and 5 git commands to generate.