Вычисление количества раз, которое число должно быть добавлено к координате для изменяемого значения этажа - PullRequest
2 голосов
/ 30 апреля 2020

У меня есть проблема, которая требует, чтобы я вычислял, сколько раз число должно быть добавлено к координате x для изменяемого значения x. Он должен поддерживать добавление как положительных, так и отрицательных чисел.

Приведенный ниже пример кода подчеркивает проблему.

public static void main(String[] args) {
    double x = 10;
    double amountToAdd = -0.25; // must support positive + negative values
    int lastFlooredX = Integer.MIN_VALUE;
    int i = 0;

    while (i++ < 100) {
        int flooredX = (int) Math.floor(x);
        if (lastFlooredX == flooredX) {
            throw new UnsupportedOperationException("Iterated over the same number twice!");
        }
        lastFlooredX = flooredX;

        int additionsBeforeFloorChanges = // I need help!;

        x += (amountToAdd * additionsBeforeFloorChanges);
    }
}

Ответы [ 2 ]

2 голосов
/ 01 мая 2020

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

Рассмотрим этот простой подход для + ve x и d:

double xfloor = Math.floor(x);
int count = 0;
do
{
    x += d;
    count += 1;
    System.out.println(count + " " + x);
}
while(Math.floor(x) == xfloor);
System.out.format("Result: %d%n", count);

При x = 0 и d = 0.1 это дает:

1 0.1
2 0.2
3 0.30000000000000004
4 0.4
5 0.5
6 0.6
7 0.7
8 0.7999999999999999
9 0.8999999999999999
10 0.9999999999999999
11 1.0999999999999999
Result: 11

Что, очевидно, неверно.

В качестве альтернативы мы можем использовать деление, чтобы определить количество необходимых шагов. Мы должны быть осторожны, чтобы различать guish между случаями, когда x и d имеют одинаковые или противоположные знаки.

static int minAddFloorChange(double x, double d)
{
    if(d == 0) throw new IllegalArgumentException();

    double xa = Math.abs(x); 
    double da = Math.abs(d);

    if(x == 0 || (Math.signum(x) == Math.signum(d)))
    {           
        double xfrac = (xa == Math.rint(xa)) ? 1 : Math.ceil(xa) - xa;
        double count = xfrac / da;
        return (int)Math.ceil(count);
    }
    else
    {
        double xfrac = (x == Math.rint(xa)) ? 0 : xa - Math.floor(xa);
        double count = xfrac / da;
        return Math.max(1, (int)Math.ceil(count));
    }
}

Обратите внимание, я не утверждаю, что этот подход полностью защищен от ошибок округления - вам нужен кто-то с большим знанием формата IEEE 754 , чем у меня чтобы решить эту проблему.

Тест:

static void test(double x, double d)
{
    System.out.println(x + " : " +  d + " = " + minAddFloorChange(x, d));
}

public static void main(String[] args)
{
    test(0, 0.1);
    test(0, -0.1);

    test(1, 0.1);
    test(-1, -0.1);

    test(1, -0.1);
    test(-1, 0.1);

    test(0, +Double.MIN_VALUE);
    test(0, -Double.MIN_VALUE);

    test(1, Double.MIN_VALUE);
    test(-1, -Double.MIN_VALUE);

    test(1, -Double.MIN_VALUE);
    test(-1, Double.MIN_VALUE);     
}

Выход:

0.0 : 0.1 = 10
0.0 : -0.1 = 10
1.0 : 0.1 = 10
-1.0 : -0.1 = 10
1.0 : -0.1 = 1
-1.0 : 0.1 = 1
0.0 : 4.9E-324 = 2147483647
0.0 : -4.9E-324 = 2147483647
1.0 : 4.9E-324 = 2147483647
-1.0 : -4.9E-324 = 2147483647
1.0 : -4.9E-324 = 1
-1.0 : 4.9E-324 = 1
1 голос
/ 30 апреля 2020
public static void main(String[] args) {
        double x = 10.1;
        double amountToAdd = -0.25; // must support positive+negative values
        double y = x;
        int i = 0;

        if(x-Math.floor(x)!=0) {
          while(Math.abs(Math.floor(y)-Math.floor(x))==0) {
             i++;
             y = y +amountToAdd;    
          }
        }
        System.out.println(i);
    }

для amountToAdd для выхода -0,25 и x для 10,1

1

для amountToAdd для выхода 0,25 и x для 10,1

4
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...