From: Patrick Tasse Date: Tue, 23 Aug 2016 20:26:13 +0000 (-0400) Subject: common: Add saturated arithmetic for int values X-Git-Url: http://git.efficios.com/?a=commitdiff_plain;h=3acbb201b86730232ba39fd5787fcef1be7f6a36;p=deliverable%2Ftracecompass.git common: Add saturated arithmetic for int values Change-Id: If59537b402015ab41db3ffe92b5a2171840f26bd Signed-off-by: Patrick Tasse Reviewed-on: https://git.eclipse.org/r/79564 Reviewed-by: Hudson CI Reviewed-by: Bernd Hufmann Tested-by: Bernd Hufmann --- diff --git a/common/org.eclipse.tracecompass.common.core.tests/src/org/eclipse/tracecompass/common/core/tests/math/SaturatedArithmeticTest.java b/common/org.eclipse.tracecompass.common.core.tests/src/org/eclipse/tracecompass/common/core/tests/math/SaturatedArithmeticTest.java index b6db807b3c..57fd5ab74a 100644 --- a/common/org.eclipse.tracecompass.common.core.tests/src/org/eclipse/tracecompass/common/core/tests/math/SaturatedArithmeticTest.java +++ b/common/org.eclipse.tracecompass.common.core.tests/src/org/eclipse/tracecompass/common/core/tests/math/SaturatedArithmeticTest.java @@ -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. + *

+ * 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)); } diff --git a/common/org.eclipse.tracecompass.common.core/src/org/eclipse/tracecompass/common/core/math/SaturatedArithmetic.java b/common/org.eclipse.tracecompass.common.core/src/org/eclipse/tracecompass/common/core/math/SaturatedArithmetic.java index 9226d36b13..a57533f003 100644 --- a/common/org.eclipse.tracecompass.common.core/src/org/eclipse/tracecompass/common/core/math/SaturatedArithmetic.java +++ b/common/org.eclipse.tracecompass.common.core/src/org/eclipse/tracecompass/common/core/math/SaturatedArithmetic.java @@ -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 + * Saturation arithmetic + */ + 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 + * Saturation arithmetic + */ + 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 * Saturation arithmetic - * @since 2.0 */ public static final long add(final long left, final long right) { long retVal = left + right;