mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
classlib: add Math methods for no-overflow addition/multiplication (#859)
Co-authored-by: Jörg Hohwiller <hohwille@users.sourceforge.net>
This commit is contained in:
parent
8fa1a86728
commit
cd14ece14c
|
@ -164,6 +164,137 @@ public final class TMath extends TObject {
|
|||
return a - floorDiv(a, b) * b;
|
||||
}
|
||||
|
||||
public static int incrementExact(int a) {
|
||||
if (a == Integer.MAX_VALUE) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return a + 1;
|
||||
}
|
||||
|
||||
public static long incrementExact(long a) {
|
||||
if (a == Long.MAX_VALUE) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return a + 1L;
|
||||
}
|
||||
|
||||
public static int decrementExact(int a) {
|
||||
if (a == Integer.MIN_VALUE) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return a - 1;
|
||||
}
|
||||
|
||||
public static long decrementExact(long a) {
|
||||
if (a == Long.MIN_VALUE) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return a - 1L;
|
||||
}
|
||||
|
||||
public static int negateExact(int a) {
|
||||
if (a == Integer.MIN_VALUE) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return -a;
|
||||
}
|
||||
|
||||
public static long negateExact(long a) {
|
||||
if (a == Long.MIN_VALUE) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return -a;
|
||||
}
|
||||
|
||||
public static int toIntExact(long value) {
|
||||
if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return (int) value;
|
||||
}
|
||||
|
||||
public static int addExact(int a, int b) {
|
||||
int sum = a + b;
|
||||
if ((a ^ sum) < 0 && (a ^ b) >= 0) { // a and b samesigned, but sum is not
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static long addExact(long a, long b) {
|
||||
long sum = a + b;
|
||||
if ((a ^ sum) < 0 && (a ^ b) >= 0) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static int subtractExact(int a, int b) {
|
||||
int result = a - b;
|
||||
if ((a ^ result) < 0 && (a ^ b) < 0) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static long subtractExact(long a, long b) {
|
||||
long result = a - b;
|
||||
if ((a ^ result) < 0 && (a ^ b) < 0) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int multiplyExact(int a, int b) {
|
||||
if (b == 1) {
|
||||
return a;
|
||||
} else if (a == 1) {
|
||||
return b;
|
||||
} else if (a == 0 || b == 0) {
|
||||
return 0;
|
||||
}
|
||||
int total = a * b;
|
||||
if (total / b != a || (a == Integer.MIN_VALUE && b == -1) || (b == Integer.MIN_VALUE && a == -1)) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
public static long multiplyExact(long a, int b) {
|
||||
return multiplyExact(a, (long) b);
|
||||
}
|
||||
|
||||
public static long multiplyExact(long a, long b) {
|
||||
if (b == 1) {
|
||||
return a;
|
||||
} else if (a == 1) {
|
||||
return b;
|
||||
} else if (a == 0 || b == 0) {
|
||||
return 0;
|
||||
}
|
||||
long total = a * b;
|
||||
if (total / b != a || (a == Long.MIN_VALUE && b == -1) || (b == Long.MIN_VALUE && a == -1)) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
public static int divideExact(int a, int b) {
|
||||
int q = a / b;
|
||||
if ((a & b & q) < 0) { // all 3 are negative
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
public static long divideExact(long a, long b) {
|
||||
long q = a / b;
|
||||
if ((a & b & q) < 0) { // all 3 are negative
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static double random() {
|
||||
if (PlatformDetector.isC()) {
|
||||
|
|
|
@ -107,6 +107,94 @@ public final class TStrictMath extends TObject {
|
|||
return TMath.round(a);
|
||||
}
|
||||
|
||||
public static int floorDiv(int a, int b) {
|
||||
return TMath.floorDiv(a, b);
|
||||
}
|
||||
|
||||
public static long floorDiv(long a, int b) {
|
||||
return TMath.floorDiv(a, b);
|
||||
}
|
||||
|
||||
public static long floorDiv(long a, long b) {
|
||||
return TMath.floorDiv(a, b);
|
||||
}
|
||||
|
||||
public static int floorMod(int a, int b) {
|
||||
return TMath.floorMod(a, b);
|
||||
}
|
||||
|
||||
public static int floorMod(long a, int b) {
|
||||
return TMath.floorMod(a, b);
|
||||
}
|
||||
|
||||
public static long floorMod(long a, long b) {
|
||||
return TMath.floorMod(a, b);
|
||||
}
|
||||
|
||||
public static int addExact(int a, int b) {
|
||||
return TMath.addExact(a, b);
|
||||
}
|
||||
|
||||
public static long addExact(long a, long b) {
|
||||
return TMath.addExact(a, b);
|
||||
}
|
||||
|
||||
public static int subtractExact(int a, int b) {
|
||||
return TMath.subtractExact(a, b);
|
||||
}
|
||||
|
||||
public static long subtractExact(long a, long b) {
|
||||
return TMath.subtractExact(a, b);
|
||||
}
|
||||
|
||||
public static int multiplyExact(int a, int b) {
|
||||
return TMath.multiplyExact(a, b);
|
||||
}
|
||||
|
||||
public static long multiplyExact(long a, int b) {
|
||||
return TMath.multiplyExact(a, b);
|
||||
}
|
||||
|
||||
public static long multiplyExact(long a, long b) {
|
||||
return TMath.multiplyExact(a, b);
|
||||
}
|
||||
|
||||
public static int divideExact(int a, int b) {
|
||||
return TMath.divideExact(a, b);
|
||||
}
|
||||
|
||||
public static long divideExact(long a, long b) {
|
||||
return TMath.divideExact(a, b);
|
||||
}
|
||||
|
||||
public static int incrementExact(int a) {
|
||||
return TMath.incrementExact(a);
|
||||
}
|
||||
|
||||
public static long incrementExact(long a) {
|
||||
return TMath.incrementExact(a);
|
||||
}
|
||||
|
||||
public static int decrementExact(int a) {
|
||||
return TMath.decrementExact(a);
|
||||
}
|
||||
|
||||
public static long decrementExact(long a) {
|
||||
return TMath.decrementExact(a);
|
||||
}
|
||||
|
||||
public static int negateExact(int a) {
|
||||
return TMath.negateExact(a);
|
||||
}
|
||||
|
||||
public static long negateExact(long a) {
|
||||
return TMath.negateExact(a);
|
||||
}
|
||||
|
||||
public static int toIntExact(long value) {
|
||||
return TMath.toIntExact(value);
|
||||
}
|
||||
|
||||
public static double random() {
|
||||
return TMath.random();
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
package org.teavm.classlib.java.lang;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.SkipPlatform;
|
||||
|
@ -197,4 +200,89 @@ public class MathTest {
|
|||
sameFloat(0.0f, Math.max(-0.0f, 0.0f));
|
||||
sameFloat(0.0f, Math.max(0.0f, -0.0f));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exacts() {
|
||||
try {
|
||||
Math.incrementExact(Integer.MAX_VALUE);
|
||||
fail();
|
||||
} catch (ArithmeticException e) {
|
||||
// ok
|
||||
}
|
||||
try {
|
||||
Math.negateExact(Integer.MIN_VALUE);
|
||||
fail();
|
||||
} catch (ArithmeticException e) {
|
||||
// ok
|
||||
}
|
||||
try {
|
||||
Math.toIntExact((long) Integer.MAX_VALUE + 1);
|
||||
fail();
|
||||
} catch (ArithmeticException e) {
|
||||
// ok
|
||||
}
|
||||
try {
|
||||
Math.addExact(Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||
fail();
|
||||
} catch (ArithmeticException e) {
|
||||
// ok
|
||||
}
|
||||
try {
|
||||
Math.subtractExact(Integer.MIN_VALUE, 2);
|
||||
fail();
|
||||
} catch (ArithmeticException e) {
|
||||
// ok
|
||||
}
|
||||
try {
|
||||
Math.multiplyExact(Integer.MIN_VALUE, -1);
|
||||
fail();
|
||||
} catch (ArithmeticException e) {
|
||||
// ok
|
||||
}
|
||||
try {
|
||||
Math.multiplyExact(Integer.MIN_VALUE, 2);
|
||||
fail();
|
||||
} catch (ArithmeticException e) {
|
||||
// ok
|
||||
}
|
||||
try {
|
||||
Math.multiplyExact(1 << 30, 2);
|
||||
fail();
|
||||
} catch (ArithmeticException e) {
|
||||
// ok
|
||||
}
|
||||
try {
|
||||
Math.divideExact(Integer.MIN_VALUE, -1);
|
||||
fail();
|
||||
} catch (ArithmeticException e) {
|
||||
// ok
|
||||
}
|
||||
IntStream.rangeClosed(-10, 10).forEach(x -> {
|
||||
assertEquals(x + 1, Math.incrementExact(x));
|
||||
assertEquals(x - 1, Math.decrementExact(x));
|
||||
assertEquals(-x, Math.negateExact(x));
|
||||
IntStream.rangeClosed(-10, 10).forEach(y -> {
|
||||
assertEquals(x + y, Math.addExact(x, y));
|
||||
assertEquals(x - y, Math.subtractExact(x, y));
|
||||
assertEquals(x * y, Math.multiplyExact(x, y));
|
||||
if (y != 0) {
|
||||
assertEquals(x / y, Math.divideExact(x, y));
|
||||
}
|
||||
});
|
||||
});
|
||||
LongStream.rangeClosed(-10, 10).forEach(x -> {
|
||||
assertEquals(x + 1, Math.incrementExact(x));
|
||||
assertEquals(x - 1, Math.decrementExact(x));
|
||||
assertEquals(-x, Math.negateExact(x));
|
||||
assertEquals((int) x, Math.toIntExact(x));
|
||||
LongStream.rangeClosed(-10, 10).forEach(y -> {
|
||||
assertEquals(x + y, Math.addExact(x, y));
|
||||
assertEquals(x - y, Math.subtractExact(x, y));
|
||||
assertEquals(x * y, Math.multiplyExact(x, y));
|
||||
if (y != 0) {
|
||||
assertEquals(x / y, Math.divideExact(x, y));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user