如何在Java中禁用转换和促销?

时间:2014-07-31 11:51:52

标签: java unsigned addition promotions

确定。我认为这是不可能的。如果您认为相同,则无需发布答案。我已经阅读了几行Chapter 5. Conversions and Promotions,似乎第5章没有提到在Java中禁用转换和促销。

这是我的动力:

long uADD(long a, long b) {
    try {
        long c;
        c = 0;
        boolean carry; //carry flag; true: need to carry; false: no need to carry
        carry = false;
        for (int i = 0; i < 64; ++i) { //i loops from 0 to 63,
            if (((((a >>> i) & 1) ^ ((b >>> i)) & 1) != 0) ^ carry) { //calculate the ith digit of the sum
                c += (1 << i);
            }
            if (((((a >>> i) & 1) & ((b >>> i) & 1)) != 0) || (carry && ((((a >>> i) & 1) ^ ((b >>> i) & 1)) != 0))) {
                carry = true; //calculate the carry flag which will be used for calculation of the (i+1)th digit
            } else {
                carry = false;
            }
        }
        if (carry) { //check if there is a last carry flag
            throw new ArithmeticException(); //throw arithmetic exception if true
        }
        return c;
    } catch (ArithmeticException arithmExcep) {
        throw new ArithmeticException("Unsigned Long integer Overflow during Addition");
    }
}

基本上,我正在编写一个方法,对长整数进行无符号加法。如果溢出,它将抛出算术异常。上面的代码不够可读,所以我应该尝试解释它。

首先,有一个for循环,其中i0循环到63

然后,第一个if语句作为完整加法器的总和输出,它使用i的{​​{1}}位数和a和{b的数字。 1}}标记以计算carry个数字(i + 1true)。 (请注意,false对应于单位数字。)如果i = 0,它会将true添加到1 << i,其中c最初为c

之后,第二个0语句充当全加器的进位标志输出,它再次使用if的{​​{1}}位和i的{​​{1}}位数。和a标志来计算b数字的carry标志。如果carry,请将新的i + 1标记设置为true,如果carry,则设置新的true标记false

最后,在退出carry循环后,检查false标志是否为for。如果carry,则抛出算术异常。

但是,上面的代码不起作用。调试后,结果发现问题出现在

true

正确的代码应该是:

true

因为Java会自动将整数c += (1 << i); 提升为Long并将其添加到c += (1L << i); ,但不会向我显示警告。

我对此有几个问题。

  1. 是否可以禁用自动将一种数据类型提升为另一种数据类型
  2. 自动促销多久会给您造成一次问题?
  3. 是否可以调整IDE,以便在自动升级时向我显示警告? (我目前正在使用NetBeans IDE 7.3.1。)
  4. 很抱歉有很多问题和难以阅读的代码。我将在9月份学习CS,所以我尝试用Java编写一些代码来熟悉Java。

1 个答案:

答案 0 :(得分:2)

  

是否可以禁用自动将一种数据类型提升为另一种数据类型

否:正如您已经发现Java语言规范要求进行数字升级,执行此操作的任何编译器(根据定义)都不是有效的Java编译器。

  

自动促销会多久给您造成一次问题?

也许每年一次(我用Java编写代码)?

  

是否可以调整IDE,以便在自动升级时向我显示警告? (我目前正在使用NetBeans IDE 7.3.1。)

值得注意的是,这样的警告不会检测到需要明确宣传的所有情况。例如,考虑:

boolean isNearOrigin(int x, int y, int r) {
    return x * x + y + y < r * r;
}

即使没有自动升级,乘法可能会溢出,这可能会使方法返回不正确的结果,并且应该写一个

    return (long) x * x + (long) y + y < (long) r * r;

代替。

还值得注意的是,您提出的警告也会出现在正确的代码中。例如:

int x = ...;
foo(x); 
如果使用参数类型foo声明long

会警告自动提升,即使该促销不会产生任何不利影响。由于这种无辜的情况非常频繁,你的警告可能会非常烦人,所以每个人都会把它关掉。因此,我很惊讶地发现任何Java编译器都会发出这样的警告。

通常,编译器无法检测到操作是否会溢出,甚至找到可能的溢出候选者也很复杂。鉴于溢出相关问题的罕见性,这种不完美的检测似乎是一个可疑的好处,这可能是Java编译器和IDE没有实现它的原因。因此,程序员仍然有责任为每个算术运算验证操作数类型提供的值集合是否合适。这包括为用作操作数的任何数字文字指定合适的类型后缀。

PS:虽然我对你的波纹进位加法器的工作印象深刻,但我认为你的uAdd方法可以更容易地实现如下:

long uAdd(long a, long b) {
    long sum = a + b;
    if (uLess(sum, a)) {
        throw new ArithmeticException("Overflow");
    } else {
        return sum;
    }
}

/** @return whether a < b, treating both a and b as unsigned longs */
boolean uLess(long a, long b) {
    long signBit = 1L << -1;
    return (signBit ^ a) < (signBit ^ b);
}

要了解为什么这是正确的,让&lt;表示签名解释的小于关系(相当于Java运算符),«表示无符号值的小于关系。设a和b为任何位模式,从中可以得到一个&#39;和b&#39;通过翻转符号位获得。通过有符号整数的定义,我们得到:

  • 如果符号(a)=符号(b),我们有(a«b)=(a&#39;«b&#39;)=(a&#39;&lt; b&#39;)
  • 如果符号(a)≠符号(b),我们有(a«b)=(b&#39;«a&#39;)=(a&#39;&lt; b&#39;)

因此,(a«b)=(a&#39;&lt; b&#39;)。