Разделение длинных строк, возвращаемых методом — неправильные значения

#java #overflow #primitive-types

#java #переполнение #примитивные типы

Вопрос:

У меня есть два метода: power и factorial:

 public static long pow(int x, int n) {
    long p = x;
    for (int i = 1; i < n; i  ) {
        p *= x;
    }
    return p;
}

public static long fact(int n) {
    long s = n;
    for (int i = 1; i < n; i   ) {
        s *= i;
    }
    return s;
}
 

которые возвращают длинные строки. Когда я хочу использовать их в новом методе, оценивающем экспоненциальную функцию, я получаю неправильные результаты по сравнению с Math.exp (x). Мой код:

 public static void exp(int x, double eps) {
    int i = 1;
    double pow = 1.0;
    double fact = 1.0;
    double sum = 0.0;
    double temp;
    do {
        temp = pow/fact;
        sum  = temp;
        pow = pow(x, i);
        fact = fact(i);
        i  ;
    }
    while (temp > eps);
    System.out.println("Check: "   Math.exp(x));
    System.out.println("My: "   sum);
}

public static void main() {
    int x = 10;
    double eps = 0.0000000000001;

    exp(x, eps);
}
 

и вывод для x = 10 равен:

Проверка: 22026.465794806718

Мой: 21798.734894914145

чем больше x, тем больше «потеря точности» (не совсем, потому что вы не можете назвать это точным …).

Поворот в том, что когда методы power и factorial возвращают double, тогда результат правильный. Кто-нибудь может объяснить мне, как заставить это работать?

Методы pow и fact должны возвращать long, и я должен использовать их в exp (назначение колледжа).

Ответ №1:

Если вы попробуете этот метод pow:

 public static long pow(int x, int n) {
    long p = x;
    System.out.println("Pow: " x "," n);
    for (int i = 1; i < n; i  ) {
        p *= x;
        System.out.println(p);
    }
    return p;
}
 

Вы получаете этот вывод:

 ...
Pow: 10,20
100
1000
10000
...
...
1000000000000000
10000000000000000
100000000000000000
1000000000000000000
-8446744073709551616
7766279631452241920
 

Длинное значение переполняется: 10^20 слишком велико, чтобы поместиться в long .

Методы pow и fact должны возвращать long, и я должен использовать их в exp (назначение колледжа).

Тогда вы мало что можете сделать, чтобы это исправить. Вы можете создать исключение, если eps оно слишком мало.

Комментарии:

1. Ну, ничего себе, вы правы. Для меня странно, что long в этом случае диапазон меньше, чем double тот, который ведет себя правильно… Спасибо.

2. @hodak — Что вы имеете в виду? long имеет меньший диапазон (~ 2 ^ 63), чем double (~ 2 ^ 1023).

3. Обе функции переполняются, в зависимости от размера параметров — pow() растет быстрее. Вы можете использовать BigDecimal и определить ситуацию, но вы вряд ли сможете поместить результат в long.

Ответ №2:

Насколько велик x обычно? Это может быть целочисленное переполнение. Попробуйте вместо этого изменить все int аргументы в pow и fact быть long .

Комментарии:

1. x невелик, он от 1 до, скажем, 20. Изменение int s на long s не помогло.

Ответ №3:

Длинные типы данных не могут обрабатывать десятичную точность, поэтому ваши значения неверны с long. Почему бы вам просто не заставить функции возвращать двойные значения?

Редактировать: вот что я придумал:

   public static long pow(int x, int n) 
  {
    double p = x;
    for (int i = 1; i < n; i  ) {
      p *= x;
    }
    return (long)p;
  }

  public static long fact(int n) 
  {
    double s = n;
    for (int i = 1; i < n; i   ) {
      s *= i;
    }
    return (long)s;
  }


  public static void exp(int x, double eps) 
  {
    double pow = 1.0;
    double fact = 1.0;
    double sum = 0.0;
    double temp;
    for(int ii=1; ii < 100; ii  )
    {
      pow = pow(x, ii);
      fact = fact(ii);
      temp = (double)pow/(double)fact;
      temp = temp == 1 ? 0 : temp;
      sum  = temp;
    }

    System.out.println("Check: "   Math.exp(x));
    System.out.println("My: "   sum);
  }

  public static void main(final String[] args)
  {
    int x = 10;
    double eps = 0.0000000000001;

    exp(x, eps);
  }
 

Это самое близкое, что вы получите, не используя десятичные дроби.

 Check: 22026.465794806718
My: 21946.785573087538
 

Комментарии:

1. Я не могу, в моем назначении сказано использовать длинные строки. Но я тоже не смог их использовать, когда пытался сделать это так: ‘pow = (double) power (x, i)’ и ‘fact = (double)fact (i)’

2. @hodak кажется очень странным, что вы не можете использовать удвоения в назначении, вы уверены, что это правильно?

3. Да, он специально заявил об этом, но возможно, что он сделал это, чтобы заставить нас найти ответ, почему это неверно. Хороший учитель, это заняло у меня всего пару часов 😉