【问题标题】:Java - floating point calc in functional styleJava - 函数式浮点计算
【发布时间】:2019-11-18 17:18:09
【问题描述】:

我猜这里 smsm2 由于浮点限制不相等:

double sm = -1.22 + (0.6027852837247973 + 0.8920332205475238);
double sm2 = -1.22 + 0.6027852837247973 + 0.8920332205475238;
System.out.println("sm = "+sm);
System.out.println("sm2 = "+sm2);
System.out.println("sm-sm2 = "+(sm-sm2));

结果是:

sm = 0.27481850427232124
sm2 = 0.27481850427232113
sm-sm2 = 1.1102230246251565E-16

由于运算顺序,我猜它不为零?

无论如何,在这种情况下我想问的是 - 如何获得与此代码等效的代码:

double r2 = 0;
for(double o : ar){
  r2+=o*2;
}
r2 -= k;

功能风格(例如,像这样,但由于是第一次操作,我的输出有问题):

Arrays.stream(ar)
    .map(o -> o*2)
    .reduce(-k, Double::sum);

【问题讨论】:

  • 为什么不只做映射乘法 -> 从零开始求和 -> 从结果中减去?因此,操作顺序与循环相同。

标签: java functional-programming floating-point double precision


【解决方案1】:

正如您在问题中提到的,操作顺序很重要。使用流时,无法保证执行顺序。除此之外,您使用流的方法中还有另一个问题,那就是 identity 参数的值 - 在您的情况下是 k (我假设您想从结果中减去一些非零值)。 reduce(double identity, DoubleBinaryOperator accumulator) 中的标识和累加器函数的值必须使得以下语句始终为真:

accumulator.apply(identity, x) = x // for any value of x

具有累加器 Double::sum(与 (d1, d2) -> Double.sum(d1, d2) 相同)有效身份的唯一值是 0。

JavaDoc 参考:

double java.util.stream.DoubleStream.reduce(double identity, DoubleBinaryOperator op)

使用提供的标识值和关联累积函数对该流的元素执行归约,并返回归约后的值。这相当于:

double result = identity;
     for (double element : this stream)
         result = accumulator.applyAsDouble(result, element)
     return result;

但不限于顺序执行。 标识值必须是累加器函数的标识。这意味着对于所有 x,accumulator.apply(identity, x) 等于 x。累加器函数必须是关联函数。

这是一个终端操作。

您可以尝试这样的事情(如@VLAZ 建议的那样):

double calculate(double[] values, double k) {
    return DoubleStream.of(values).map(d -> d * 2).reduce(0, Double::sum) - k;
}


更新

正如@LouisWasserman 所建议的,calculate 方法可以通过将reduce(0, Double::sum) 替换为sum() 来简化:

double calculate(double[] values, double k) {
    return DoubleStream.of(values).map(d -> d * 2).sum() - k;
}

【讨论】:

  • DoubleStream.reduce(0, Double::sum) 可能比DoubleStream.sum() 做得更好。
  • @LouisWasserman 是的,没错。我用DoubleStream.reduce(0, Double::sum) 来强调身份的重要性。否则,DoubleStream.sum() 在这种情况下更合适,我同意。
  • @ahoxha 好的,我想我把它复杂化了......只是最后有-k看起来不太好(不知道为什么::)我也使用Arrays.stream(values).map(d -> d * 2).sum() - k;从那时起,我可以将一个类用于所有原始类型。
猜你喜欢
  • 2015-09-09
  • 2012-03-10
  • 1970-01-01
  • 2012-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-16
相关资源
最近更新 更多