2

Ok. I think this is impossible. If you think the same, you do not need to post an answer. I have read a few lines of Chapter 5. Conversions and Promotions and it seems chapter 5 has no mention of disabling Conversions and Promotions in Java.

Here is my motivation:

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");
    }
}

So basically, I am writing a method that will do unsigned addition for long integer. It will throw arithmetic exception if overflow. The code above is not readable enough, so I should try to explain it.

First, there is a for loop where i loops from 0 to 63.

Then, the first if statement acts as the sum output of the full adder, it uses the ith digit of a and that of b and carry flag to calculate the i + 1th digit (true or false). (Note that i = 0 corresponds to the units digit.) If true, it adds 1 << i to c, where c is initially 0.

After that, the second if statement acts as the carry flag output of the full adder, it uses again the ith digit of a and that of b and carry flag to calculate the carry flag of the i + 1th digit. If true, set the new carry flag to true, if false, set the new carry flag false.

Finally, after exit the for loop, check if the carry flag is true. If true, throw arithmetic exception.

However, the above code does not work. After debugging, it turns out the problem occurs at

c += (1 << i);

The correct code should be:

c += (1L << i);

because Java will automatically promote integer 1 << i to Long and add it to c, showing no warning to me.

I have several questions regarding to this.

  1. Is it possible to disable automatic promotion of one data type to another
  2. How often does automatic promotion causing problem to you?
  3. Is it possible to tweak the IDE so that it shows a warning to me when automatic promotion occurs? (I am using NetBeans IDE 7.3.1 at the moment.)

Sorry for lots of questions and the hard to read code. I will be studying CS in September so I try to write some code in Java to familiarize myself with Java.

Edd
  • 3,724
  • 3
  • 26
  • 33
Alex Vong
  • 483
  • 4
  • 15

1 Answers1

2

Is it possible to disable automatic promotion of one data type to another

No: as you already discovered the Java Language Specification mandates numeric promotion to occur, any compiler doing this would (by definition) not be a valid Java Compiler.

How often does automatic promotion causing problem to you?

Perhaps once a year (and I code in Java for a living)?

Is it possible to tweak the IDE so that it shows a warning to me when automatic promotion occurs? (I am using NetBeans IDE 7.3.1 at the moment.)

It is worth noting that such a warning would not detect all cases where an explicit promotion is needed. For instance, consider:

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

Even though there is no automatic promotion, the multiplications may overflow, which can make the method return incorrect results, and one should probably write

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

instead.

It's also worth noting that your proposed warning would also appear for correct code. For instance:

int x = ...;
foo(x); 

would warn about automatic promotion if foo is declared with parameter type long, even though that promotion can not have any adverse effects. Since such innocent situations are quite frequent, your warning would probably be so annoying that everybody would turn it off. I'd therefore by quite surprised to find any Java compiler emit such a warning.

In general, the compiler can not detect that an operation will overflow, and even finding likely candidates for overflow is complex. Given the rarity of overflow-related problems, such an imperfect detection seems a dubious benefit, which is probably why Java compilers and IDEs do not implement it. It therefore remains the responsibility of the programmer to verify, for each arithmetic operation, that the value set afforded by the operand types is suitable. This includes specifying suitable type suffixes for any numeric literals used as operands.

PS: Though I am impressed that you got your ripple-carry adder working, I think your uAdd method could be more easily implemented as follows:

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);
}

To see why this is correct, let < denote the less than relation for the signed interpretation (which is equivalent to the Java operator), and ≪ denote the less than relation for the unsigned values. Let a and b be any bit pattern, from which a' and b' are obtained by flipping the sign bit. By the definition of signed integers, we then have:

  • If sign(a) = sign(b), we have (a ≪ b) = (a' ≪ b') = (a' < b')
  • If sign(a) ≠ sign(b), we have (a ≪ b) = (b' ≪ a') = (a' < b')

Therefore, (a ≪ b) = (a' < b').

meriton
  • 68,356
  • 14
  • 108
  • 175
  • Thanks for detailed explanation and the alternative way for doing it. So it seems this type of problem is very rare even for a commercial programmer. I should try to be cautious when doing bit-wise operation. Finally,`boolean uLess(long a, long b) { long signBit = 1L << -1; return (signBit ^ a) < (signBit ^ b); }` is where all the magic happens. It seems I need some time to understand it. – Alex Vong Aug 01 '14 at 05:00
  • I added a proof sketch. – meriton Aug 01 '14 at 08:40
  • Just a typo, it should be `long uAdd(long a, long b)` I guess. – Alex Vong Aug 01 '14 at 10:27