distanceTo () целочисленное переполнение? - PullRequest
0 голосов
/ 18 февраля 2020

Это мой метод определения расстояния между двумя точками:


    // Euclidean distance between this point and that point
    public int distanceTo(Point that) {
        int distanceX = this.x - that.x;
        int distanceY = this.y - that.y;
        return (int) Math.sqrt(distanceX * distanceX + distanceY * distanceY);
    }

Возможно ли, что происходит целочисленное переполнение, и если да, как я могу предотвратить это?

РЕДАКТИРОВАТЬ:

enter image description here

Ответы [ 3 ]

2 голосов
/ 18 февраля 2020

Чтобы предотвратить неверный результат от переполнения, используйте Math «точные» методы:

1) Или вариант long.

Методы выдают ArithmeticException, если произойдет переполнение.

public int distanceTo(Point that) throws ArithmeticException {
    int distanceX = Math.subtractExact(this.x, that.x);
    int distanceY = Math.subtractExact(this.y, that.y);
    return (int) Math.sqrt(Math.addExact(Math.multiplyExact(distanceX, distanceX),
                                         Math.multiplyExact(distanceY, distanceY)));
}

Конечно, было бы разумно использовать long math, чтобы минимизировать вероятность переполнения.

public int distanceTo(Point that) {
    long distanceX = Math.subtractExact((long) this.x, (long) that.x);
    long distanceY = Math.subtractExact((long) this.y, (long) that.y);
    long sumOfSquares = Math.addExact(Math.multiplyExact(distanceX, distanceX),
                                      Math.multiplyExact(distanceY, distanceY));
    return Math.toIntExact((long) Math.sqrt(sumOfSquares));
}

Может быть небольшая потеря точности, когда sumOfSquares увеличено до double, но эффект, вероятно, будет потерян, если отбрасывать десятичные дроби во время приведения к long.

0 голосов
/ 18 февраля 2020

Вы можете определить метод для проверки переполнения int. Краткая демонстрация выглядит следующим образом:

class Main {
    public static void main(String[] args) {
        // Test
        int x = 0;
        System.out.println("Square of " + x + " overflows int: " + isIntOverflowForSquare(x));
        x = 123;
        System.out.println("Square of " + x + " overflows int: " + isIntOverflowForSquare(x));
        x = 1234567890;
        System.out.println("Square of " + x + " overflows int: " + isIntOverflowForSquare(x));
        x = Integer.MAX_VALUE;
        System.out.println("Square of " + x + " overflows int: " + isIntOverflowForSquare(x));
    }

    static boolean isIntOverflowForSquare(int x) {
        if (x == 0)
            return false;

        int square = x * x;
        if (x == square / x)
            return false;
        else
            return true;
    }
}

Вывод:

Square of 0 overflows int: false
Square of 123 overflows int: false
Square of 1234567890 overflows int: true
Square of 2147483647 overflows int: true
0 голосов
/ 18 февраля 2020

Сначала вы можете использовать функцию гипотенуза .

Тогда на расстоянии может произойти целочисленное переполнение (-).

решение состоит в том, чтобы использовать double, так как конечный результат вычисляется с использованием функции с плавающей запятой.

Результат может быть таким же большим, как sqrt(2)*2*Integer.MAX_VALUE, также переполненным.

Итак:

public int distanceTo(Point that) {
    double distanceX = ((double)this.x) - that.x;
    double distanceY = ((double)this.y) - that.y;
    double distance = Math.hypot(distanceX, distanceY);
    if (distance + 1 >= Integer.MAX_VALUE) {
        throw new ArithmeticException("Integer overflow");
    }
    return (int) distance; // (int) Math.round?
}

Или аккуратнее (как и Андреас):

public int distanceTo(Point that) {
    double distanceX = ((double)this.x) - that.x;
    double distanceY = ((double)this.y) - that.y;
    double distance = Math.hypot(distanceX, distanceY);
    return Math.toIntExact((long)distance); // (int) Math.round?
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...