Java:用assert实现长类型溢出免疫

标签 java jvm assert assertions

这是我计算有理数不同运算的代码。现在,我的任务是使用 assert 实现额外的安全性,以防止其溢出。我尝试做一些事情,但是当我使用 -ea 启用 JVM 断言时,它开始一直“抛出”断言(即使使用 4 > 5 断言条件) 。 我如何实现这一点以及在 数字上执行此操作的正确方法是什么?

public class Rational {

private final long numerator;
private final long denominator;
private double result;

public Rational(long numerator, long denominator){
    this.numerator = numerator;
    this.denominator = denominator;
    result = (double)numerator/(double)denominator;
}

public Rational plus(Rational b){
    assert this.denominator * b.denominator >= Long.MAX_VALUE : "Overflow of denominator in 'PLUS'"; //My attempt

    long plusDenominator = this.denominator * b.denominator;
    long plusNumerator = ((plusDenominator / this.denominator) * this.numerator) + ((plusDenominator / b.denominator) * b.numerator);
    long gcd = gcd(plusNumerator, plusDenominator);
    return new Rational(plusNumerator / gcd, plusDenominator / gcd);
}

public Rational minus(Rational b){
    long minusDenominator = this.denominator * b.denominator;
    long minusNumerator = ((minusDenominator / this.denominator) * this.numerator) - ((minusDenominator / b.denominator) * b.numerator);
    long gcd = gcd(minusNumerator, minusDenominator);
    return new Rational(minusNumerator / gcd, minusDenominator / gcd);
}

public Rational times(Rational b){
    long timesDenominator = this.denominator * b.denominator;
    long timesNumerator = this.numerator * b.numerator;

    long gcd = gcd(timesDenominator, timesNumerator);
    return new Rational(timesNumerator / gcd, timesDenominator / gcd);
}

public Rational divides(Rational b){
    long divDenominator = this.denominator * b.numerator;
    long divNumerator = this.numerator * b.denominator;

    long gcd = gcd(divNumerator, divDenominator);
    return new Rational(divNumerator / gcd, divDenominator / gcd);
}

private long gcd(long p, long q){
    if(q == 0) return p;
    long r = p % q;
    return gcd(q, r);
}

public static void main(String[] args){
    Rational r1 = new Rational(8, 999999999999999999L);
    Rational r2 = new Rational(8 ,999999999999999999L);
    System.out.println(r1.plus(r2));
}

}

最佳答案

测试如下:

assert this.denominator * b.denominator >= Long.MAX_VALUE . . .

不可能是对的。如果 this.denominator * b.denominator 溢出,结果可能为负;它肯定不能大于 Long.MAX_VALUE 并且几乎不可能等于 Long.MAX_VALUE。您需要一种更有效的方法来检测是否会发生溢出。

通常的方法是向上转换为更大的整数大小或进行预测试。由于您已经使用了long,唯一可用的向上转换是BigInteger,我假设您不想使用它。以下是如何使用预测试:

static final long safeMultiply(long left, long right)
                 throws ArithmeticException {
  if (right > 0
         ? left > Long.MAX_VALUE/right || left < Long.MIN_VALUE/right 
         : (right < -1
             ? left > Long.MIN_VALUE/right  || left < Long.MAX_VALUE/right
             : right == -1 && left == Long.MIN_VALUE) ) {
      throw new ArithmeticException("Long overflow");
  }
  return left * right;
}

我根本不建议为此使用 assert,因为它可以在运行时关闭。您可以在某处维护一个标志,是否发出溢出信号并在其为 false 时跳过测试。

关于Java:用assert实现长类型溢出免疫,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18434031/

相关文章:

java - javac 会生成静态桥接方法吗?

c++ - 如何做出健壮的断言?

Java String to Date,DateFormat 不工作

java - 如何在 selenium webdriver 中处理 Javascript 警报/弹出窗口

java - 向正在运行的 JVM 发送信号

java - Hamcrest:如何测试一个对象的多个属性

java - 强制 assertEquals(String s, Object o) 使用 o.toString()?

java - 如何在 Scala 中使用 Java 包 com.example...object

java - Swift iOS 不确定应用程序或远程服务器是否应该处理 amazon s3

java - 以独立于平台的方式将环境变量传递给 JVM