2

好的。我认为这是不可能的。如果您有相同的想法,则无需发布答案。我已经阅读了第 5 章的几行。转换和促销,似乎第 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循环ifrom063

然后,第一if条语句作为全加器的和输出,它使用 的i第 位a和的第 位bcarry标志来计算i + 1第 位(truefalse)。(请注意,i = 0对应于个位。)如果true,它添加1 << ic,其中c最初是0

之后,第二if条语句作为全加器的进位标志输出,它再次使用 的i第 位aband carryflag 的第 1 位来计算第 1 位的carry标志i + 1位。如果true,设置新carry标志true,如果false,设置新carry标志false

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

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

c += (1 << i);

正确的代码应该是:

c += (1L << i);

因为Java会自动将整数提升1 << i为 Long 并将其添加到c,对我没有任何警告。

我对此有几个问题。

  1. 是否可以禁用将一种数据类型自动提升为另一种数据类型
  2. 自动促销多久给您带来问题?
  3. 是否可以调整 IDE,以便在发生自动升级时向我显示警告?(我目前使用的是 NetBeans IDE 7.3.1。)

很抱歉有很多问题和难以阅读的代码。我将在 9 月份学习 CS,所以我尝试用 Java 编写一些代码来熟悉 Java。

4

1 回答 1

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如果使用参数 type 声明,则会警告自动升级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);
}

要了解为什么这是正确的,让 < 表示有符号解释的小于关系(相当于 Java 运算符),≪ 表示无符号值的小于关系。令 a 和 b 为任意位模式,通过翻转符号位从中获得 a' 和 b'。根据有符号整数的定义,我们有:

  • 如果 sign(a) = sign(b),我们有 (a ≪ b) = (a' ≪ b') = (a' < b')
  • 如果 sign(a) ≠ sign(b),我们有 (a ≪ b) = (b' ≪ a') = (a' < b')

因此,(a ≪ b) = (a' < b')。

于 2014-07-31T15:55:34.687 回答