Java - 舍入数字的问题

时间:2009-06-01 19:24:51

标签: java ruby

我编写了一些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的一个是正确的)。这是我打电话给r(4):

爪哇:

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

红宝石:

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

为什么powS可以在xk中正确计算,而不是maxVal.intValue()?请注意,在Java版本上,我需要使用maxVal而不是Double,否则返回零。我也尝试用BigDecimal替换{{1}} s也无济于事。

6 个答案:

答案 0 :(得分:3)

当您致电maxVal.intValue()

时,您会收到截断错误

看一下BigDecimal和BigInteger,就像你的ruby片段一样。

顺便说一句:如果你使用位于Java之上的groovy,那么它会立即使用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

答案 1 :(得分:2)

我的Java生锈了,但intValue()没有转到32位的int值? 2 ^ 46太大而不适合那里。

答案 2 :(得分:2)

当你调用intValue()时,double值将被强制转换为int。

Java的整数MAX_VALUE:

  

保持int的最大值的常量可以是2 ^ 31-1。

你的MAX_VAL:

  

2 ** 46

即使是长型也不能保持足够大的价值。我认为正如@toolkit所说,你需要java.math包中的BigInteger类。

答案 3 :(得分:2)

两件事:

  1. 在这种情况下,您需要BigInteger之类的内容来确保值不会溢出其数据类型。
  2. 在一个相关问题中,maxVal.intValue()返回2147483647,远远低于它应该的大约7.03E13。

答案 4 :(得分:1)

697573698804632150000000000000000000000000000

697573698804632158498861826956272125244140625
数字不一样。通过拥有如此大的数字,你在Java中失去了精度,其中Ruby具有任意精度整数运算。将算术运算应用于Java中较大的数字将导致[伪]不可预测的结果。

答案 5 :(得分:1)

问题在于Double无法保留您尝试放入的值,因此它会被截断。

对于像这样的大值,你需要使用java.math.BigDecimal,这允许十进制值具有任意大的精度。

这是你使用BigDecimal重做Java样本:

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;
    }
}

此版本属性返回您正在寻找的正确结果。