Java - проблемы округления чисел - PullRequest
1 голос
/ 01 июня 2009

Я написал некоторый Java-код для генерации псевдослучайных чисел, но столкнулся с тем, что, как мне кажется, возникают проблемы с округлением, чтобы он соответствовал тому же коду в Ruby. Вот что у меня есть в Java:

public class randTest {
  private final static Double A = Math.pow(5,13);
  private final static Integer S = 314159265;
  private final static Double minVal = Math.pow(2, -46);
  private final static Double maxVal = Math.pow(2, 46);
  private final static Double newMax = 10.0;

  private static Double r(Integer k) {
    Double powS = Math.pow(A, k) * S;
    Double xk = powS % maxVal.intValue();
    Double result = minVal * xk;
    System.out.println("k = " + k + ", pows = " + powS + ", xk = " + xk + ", result = " + result);
    return result;
  }
}

И то же самое в Ruby (моя рабочая рабочая реализация):

A = 5 ** 13
S = 314159265
MIN_VAL = 2 ** -46
MAX_VAL = 2 ** 46
NEW_MAX = 10

def r(k) # was generate_random
  powS = (A ** k) * S
  xk = powS % MAX_VAL
  result = MIN_VAL * xk
  puts "k = #{k}, pows = #{powS}, xk = #{xk}, result = #{result}"
  return result
end

По какой-то причине выходные данные сильно отличаются (но Ruby правильный). Вот я звоню г (4):

Java:

k = 4, pows = 6.9757369880463215E44, xk = 1.512592341E9, result = 2.1495230001278287E-5

Ruby:

k = 4, pows = 697573698804632158498861826956272125244140625, xk = 55279057169489, result = 0.785562650228954

Есть идеи, почему powS будет правильно вычисляться в обоих, но не xk? Обратите внимание, что в Java-версии мне нужно было использовать maxVal.intValue() вместо maxVal, иначе он возвращает ноль. Я также пытался заменить Double s на BigDecimal s, но безрезультатно.

Ответы [ 6 ]

3 голосов
/ 01 июня 2009

Вы получаете ошибки усечения при вызове maxVal.intValue()

Взгляните на BigDecimal и BigInteger, чтобы добиться того же, что и ваш фрагмент ruby.

Кстати: если вы используете groovy , который находится поверх Java, тогда используется BigDecimal из коробки.

Пример кода:

public class Rounding {
    private final static BigDecimal A = BigDecimal.valueOf(Math.pow(5, 13));
    private final static int S = 314159265;
    private final static BigDecimal minVal = BigDecimal.valueOf(Math
            .pow(2, -46));
    private final static BigDecimal maxVal = BigDecimal
            .valueOf(Math.pow(2, 46));
    private final static BigDecimal newMax = BigDecimal.valueOf(10);

    public static void main(final String[] args) {
        r(4);
    }

    private static void r(final int k) {
        final BigDecimal powS = A.pow(k).multiply(BigDecimal.valueOf(S));
        final BigDecimal xk = powS.remainder(new BigDecimal(maxVal
                .toBigInteger()));
        final BigDecimal result = minVal.multiply(xk);
        System.out.println("k = " + k + ", pows = " + powS + ", xk = " + xk
                + ", result = " + result);

    }
}

Производит:

k = 4, pows = 697573698804632158498861826956272125244140625, xk = 55279057169489, result = 0.785562650228953900455100455956
2 голосов
/ 01 июня 2009

Две вещи:

  1. В такой ситуации вам понадобится что-то вроде BigInteger , чтобы убедиться, что значения не переполняют их типы данных.
  2. В связанной с этим проблеме, maxVal.intValue() возвращает 2147483647, что намного меньше, чем примерно 7.03E13.
2 голосов
/ 01 июня 2009

Когда вы вызываете intValue (), двойное значение будет приведено к int.

Java Integer MAX_VALUE:

Константа, содержащая максимальное значение, которое может иметь int, 2 ^ 31-1.

Ваш MAX_VAL:

2 ** 46

Даже тип long не может содержать значения достаточно большие. Я думаю, как говорит @toolkit, вам нужен класс BigInteger из пакета java.math.

2 голосов
/ 01 июня 2009

Моя Java ржавая, но разве intValue () не переходит к 32-битному значению int? 2 ^ 46 было бы слишком большим, чтобы туда поместиться.

1 голос
/ 01 июня 2009

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

Для больших значений, подобных этому, вам нужно использовать java.math.BigDecimal , что позволяет получить сколь угодно большую точность десятичных значений.

Вот вам пример Java, переделанный с использованием BigDecimal:

public class RandTest {

    private final static BigDecimal A = new BigDecimal(5).pow(13);
    private final static BigDecimal S = new BigDecimal(314159265);
    private final static BigDecimal minVal = 
         new BigDecimal(2).pow(-46, new MathContext(100));
    private final static BigDecimal maxVal = new BigDecimal(2).pow(46);
    private final static BigDecimal newMax = new BigDecimal(10.0);

    private static BigDecimal r(Integer k) {
        BigDecimal powS = A.pow(k).multiply(S);
        BigDecimal xk = powS.remainder(maxVal);
        BigDecimal result = minVal.multiply(xk);
        System.out.println("k = " + k + ", pows = " + powS + ", xk = " + xk
                + ", result = " + result);
        return result;
    }
}

Это свойство версии возвращает правильный результат, который вы ищете.

1 голос
/ 01 июня 2009

697573698804632150000000000000000000000000000
и
697573698804632158498861826956272125244140625
это не одно и то же число. Вы теряете точность в Java, имея такое большое число, где Ruby имеет произвольную целочисленную арифметику точности. Применение арифметических операций к большим числам в Java приведет к [псевдо-] непредсказуемым результатам.

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