Как отключить преобразования и рекламные акции в Java?

1

ОК. Я думаю, что это невозможно. Если вы считаете то же самое, вам не нужно публиковать ответ. Я прочитал несколько строк главы 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 где i от 0 до 63.

Тогда первый оператор if действует как суммарный вывод полного сумматора, он использует i ю цифру a и b и carry флаг для вычисления i + 1 й цифры (true или false). (Обратите внимание, что i = 0 соответствует значению единиц.) Если true, он добавляет 1 << i в c, где c изначально 0.

После этого второй оператор if выступает в качестве вывода флага переноса полного сумматора, он снова использует i ю цифру a и b и carry флаг для вычисления флага 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 так, чтобы она показывала мне предупреждение, когда происходит автоматическое продвижение? (В настоящее время я использую IDE 7.3.1 NetBeans).

Извините за множество вопросов и трудный для чтения код. Я буду изучать CS в сентябре, поэтому я пытаюсь написать код на Java, чтобы ознакомиться с Java.

  • 3
    Глава 5 ???? /
  • 0
    извините, забыли URL
Показать ещё 4 комментария
Теги:
addition
unsigned
promotions

1 ответ

2
Лучший ответ

Можно ли отключить автоматическое продвижение одного типа данных в другое

Нет: поскольку вы уже обнаружили, что спецификация Java Language Specification указывает на числовое продвижение, любой компилятор, выполняющий это, по определению не будет корректным компилятором Java.

Как часто автоматическое продвижение повлечет за собой проблему?

Возможно, раз в год (и я код на Java для жизни)?

Можно ли настроить среду IDE так, чтобы она показывала мне предупреждение, когда происходит автоматическое продвижение? (В настоящее время я использую IDE 7.3.1 NetBeans).

Стоит отметить, что такое предупреждение не будет выявлять все случаи, когда требуется явное продвижение по службе. Например, рассмотрим:

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

Чтобы понять, почему это правильно, пусть <обозначает меньшее, чем отношение для подписанной интерпретации (что эквивалентно оператору Java), а "означает меньшее отношение для значений без знака. Пусть a и b - любая битовая диаграмма, из которой a 'и b' получаются переворачиванием знакового бита. По определению целых чисел со знаком мы имеем:

  • Если знак (a) = знак (b), мы имеем (a "b) = (a '" b') = (a '<b')
  • Если знак (a) ≠ знак (b), мы имеем (a "b) = (b '" a') = (a '<b')

Следовательно, (a "b) = (a '<b').

  • 0
    Спасибо за подробное объяснение и альтернативный способ сделать это. Так что, похоже, проблема такого типа очень редка даже для коммерческого программиста. Я должен стараться быть осторожным при выполнении побитовой операции. Наконец, boolean uLess(long a, long b) { long signBit = 1L << -1; return (signBit ^ a) < (signBit ^ b); } происходит вся магия. Кажется, мне нужно время, чтобы понять это.
  • 0
    Я добавил пробный эскиз.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню