Как преобразование из двойного в int работает в Java? - PullRequest
5 голосов
/ 10 августа 2011

Для следующего кода (Java):

double d = (double) m / n; //m and n are integers, n>0
int i = (int) (d * n);

i == m

Всегда ли последнее выражение верно? Если это не так, всегда ли это правда?:

i = (int) Math.round(d * n);

i == m

Ответы [ 4 ]

5 голосов
/ 10 августа 2011

int i = (int) (d * n); i == m;

Это неверно для m = 1, n = 49.

i = (int) Math.round(d * n); i == m;

Моя интуиция говорит мне, что это должно быть правдой, но это может быть трудно доказать строго.

4 голосов
/ 10 августа 2011

Второй вопрос, который вы задаете, касается размера ulp в Java.

Если ulp превышает 1/(n), то округление умножения не приведет к восстановлению исходного деленного целого числа.Как правило, большие язвы связаны с большими двойными значениями.Ульпа, связанная с двойным, начинает превышать 1 в районе 9E15;если ваши восстановленные двойники были там, то вы могли бы найти проблемы с round (), не получив ожидаемого ответа.Однако, когда вы работаете со значениями типа int, наибольшее значение числителя вашего деления будет Integer.MAX_VALUE.

Следующая программа проверяет все положительные целые значения n, чтобы определить, какое из них вызываетНаибольший потенциал ошибки округления при попытке восстановить разделенное целое число:

  public static void main(String[] args)
  {
    // start with large number
    int m = Integer.MAX_VALUE;
    double d = 0;

    double largestError = 0;
    int bigErrorCause = -1;
    for (int n = 1; n < Integer.MAX_VALUE; n++)
    {
      d = (double) m / n;
      double possibleError = Math.ulp(d) * n;
      if (possibleError > largestError)
      {
        largestError = possibleError;
        bigErrorCause = n;
      }
    }
    System.out.println("int " + bigErrorCause + " causes at most "
        + largestError + " error");
  }

Выходные данные:

int 1073741823 вызывает не более 4,768371577590358E-7 ошибка

Округление с использованием Math.round, затем приведение к int должно восстановить исходное значение int.

1 голос
/ 10 августа 2011

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

Если n очень большое, оно может быть ложным, я не совсем уверен. Я знаю, что это будет правдой, по крайней мере, в 99% случаев.

1 голос
/ 10 августа 2011

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

Вам гораздо лучше сравнивать их, используя такой порог:

Math.abs( d*n - m ) < 0.000001;

Обратите внимание, что эти два утверждения должны быть эквивалентны

i = (int) (d * n);
i = (int) Math.round(d * n);

Однако, например, если d=3/2 и n=2, ошибки с плавающей запятой могут привести к i=2.999999999999, что после усечения / округления равно 2.

...