Java: результаты вращения точек не точны - PullRequest
0 голосов
/ 12 ноября 2011

У меня есть следующий код ...

public class Point {
    private double x;
    private double y;
    static private final double RADTODEG = 180.0d / Math.PI ;
    static private final double DEGTORAD = Math.PI / 180.0d;

    /**
     * Rotates the point by a specific number of radians about a specific origin point.
     * @param origin The origin point about which to rotate the point
     * @param degrees The number of radians to rotate the point
     */
    public void rotateByRadians(Point origin, double radians) {
        double cosVal = Math.cos(radians);
        double sinVal = Math.sin(radians);

        double ox = x - origin.x;
        double oy = y - origin.y;

        x = origin.x + ox * cosVal - oy * sinVal;
        y = origin.y + ox * sinVal + oy * cosVal;
    }

    /**
     * Rotates the point by a specific number of degrees about a specific origin point.
     * @param origin The origin point about which to rotate the point
     * @param degrees The number of degrees to rotate the point
     */
    public void rotateByDegrees(Point origin, double degrees) {
        rotateByRadians(origin, degrees * DEGTORAD);
    }

    /**
     * Rotates the point by the specified number of radians about the axis' origin (0,0). To rotate about a specific origin point, see rotateByRadians(Point, double)
     * @param radians Measure of radians to rotate the point
     */
    public void rotateByRadians(double radians) {
        if(isEmpty()) // Since we're rotating about 0,0, if the point is 0,0, don't do anything
            return;

        double cosVal = Math.cos(radians);
        double sinVal = Math.sin(radians);

        double newx = x * cosVal - y * sinVal;
        double newy = x * sinVal + y * cosVal;

        x = newx;
        y = newy;
    }

    /**
     * Rotates the point by the specified number of degrees about to the axis' origin (0,0). To rotate about a specific origin point, see rotateByDegrees(Point, double)
     * @param degrees Measure of degrees to rotate the point
     */
    public void rotateByDegrees(double degrees) {
        rotateByRadians(degrees * DEGTORAD);
    }

Проблема возникает, когда дается точка, скажем, 0,200. Называя вращение (относительно начала координат 0,0) на 180 градусов, оно должно быть (0, -200). Координата x не должна была измениться. Тем не менее, это в конечном итоге (-2,4492935982947064E-14, -200). Я пытался использовать strictfp, но это не имеет значения. Это влияет только на результат, если вращаемая координата равна нулю. Ненулевые значения работают нормально. Есть идеи, почему это не так?

Код ниже:

   Point p = new Point(0.0d, 200.0d);
   p.rotateByDegrees(180.0d);
   System.out.println(p);

Дает вывод:

shapelib.Point Object {x: -2.4492935982947064E-14 y: -200.0}

Ответы [ 3 ]

3 голосов
/ 12 ноября 2011

К лучшему или к худшему, так обстоит дело с математикой с плавающей запятой. От http://mindprod.com/jgloss/floatingpoint.html:

"Думайте о плавании и двойном представлении как о физических измерениях. Нет можно было бы жаловаться, если бы их столяр сделал стол 6.000000000001 футов в длину. Аналогично, не жалуйтесь на неизбежное крошечное ошибки в арифметических результатах с плавающей запятой, например, Математика соз ( Math.toRadians (90)) не выходит на ура. ( Если ты хочешь совершенство, используйте int, long, BigInteger или BigDecimal. ) "

1 голос
/ 12 ноября 2011

Оба приведенных ответа верны, но они упускают фундаментальную точку.

Диапазон возможных значений, которые может принимать число с плавающей запятой, не является непрерывным. Скорее, в нем есть дыры. Таким образом, вы можете представить, что от 0,1 до 0,2 вместо бесконечного количества чисел существует только конечное число.

Это основная причина, по которой арифметика с плавающей точкой является неточной. Компьютеры не могут точно представлять каждое действительное число, которое вы хотите. Вместо этого они могут получить только небольшой эпсилон от фактического значения.

Например, вы не можете точно представить дробь 2/10. Если вы распечатаете все десятичные разряды для этого числа, вы найдете что-то вроде 0.20000000000000001.

См. Здесь для более полной записи: http://floating -point-gui.de /

1 голос
/ 12 ноября 2011

Арифметика с плавающей точкой не является полностью точной. Ошибка мощности 10 ^ -14 достаточно в большинстве случаев. Если вы подсчитаете Math.sin(Math.PI), вы получите 1.2246467991473532E-16. Зачем вам нужно точно 0 в вашем случае?

...