Java,BigDecimal:如何对舍入错误进行单元测试?

Java,BigDecimal:如何对舍入错误进行单元测试?,java,unit-testing,precision,bigdecimal,Java,Unit Testing,Precision,Bigdecimal,我将给出一个我的实际情况的简化示例: 假设我必须用Java实现一些代码来计算。我得到了两个浮点值数组(以双精度表示),每个数组的长度相同,第一个数组包含值,第二个数组包含它们各自的权重 假设我做了一些实现,返回一个浮点值(也是一个双精度值),表示输入值的加权算术平均值: public static double calculateWeightedArithmeticMean(double[] values, double[] weights) { if(values.

我将给出一个我的实际情况的简化示例:

假设我必须用Java实现一些代码来计算。我得到了两个浮点值数组(以双精度表示),每个数组的长度相同,第一个数组包含值,第二个数组包含它们各自的权重

假设我做了一些实现,返回一个浮点值(也是一个双精度值),表示输入值的加权算术平均值:

public static double calculateWeightedArithmeticMean(double[] values, 
        double[] weights) {

    if(values.length != weights.length) {
        throw new IllegalArgumentException();
    }

    if(values.length == 0) {
        return 0;
    }

    if(values.length == 1) {
        return new BigDecimal(values[0]).setScale(1, RoundingMode.HALF_UP).
                doubleValue();
    }

    BigDecimal dividend = BigDecimal.ZERO;
    BigDecimal divisor = BigDecimal.ZERO;
    for(int i = 0; i < values.length; i++) {
        dividend = dividend.add(new BigDecimal(values[i]).
                multiply(new BigDecimal(weights[i])));
        divisor = divisor.add(new BigDecimal(weights[i]));
    }
    if(dividend.compareTo(BigDecimal.ZERO) == 0) {
        return 0d;
    }
    return dividend.divide(divisor, 1, RoundingMode.HALF_UP).doubleValue();
}
公共静态双计算加权算术平均值(双[]值,
双[]重量){
if(values.length!=weights.length){
抛出新的IllegalArgumentException();
}
如果(values.length==0){
返回0;
}
如果(values.length==1){
返回新的BigDecimal(值[0])。设置刻度(1,舍入模式。向上舍入)。
doubleValue();
}
BigDecimal股息=BigDecimal.0;
BigDecimal除数=BigDecimal.0;
对于(int i=0;i
我编写了一个单元测试,通过几个值(比如,3个值+3个权重)。我首先手动计算它们的加权算术平均值(使用计算器),然后编写一个单元测试,检查代码是否返回该值

我认为,由于舍入误差,这种测试不适用于使用的值数量大得多的情况。也许我实现的代码对于3个值+3个权重(对于给定的精度)效果很好,因为在这种情况下舍入误差小于精度,但是对于1000个值+1000个权重,舍入误差很可能大于所需的精度

我的问题是:

  • 我是否也应该编写一个单元测试来检查大量的值(生产使用的“最坏情况”场景)
  • 如果我应该,我该怎么写?如何获得正确的值,以便在我的测试断言中使用它(手动计算2x1000值的平均值似乎是个坏主意,即使使用
  • 同样的情况也适用于类似的场景:计算

在编写单元测试时,您总是不得不在某个地方放弃。诀窍是当你确信自己知道的足够多时就放弃:-)

在您的案例中,几个简单的测试用例是:

  • 空数组
  • 创建第二个算法,该算法使用精确的算术运算(如
    bigdecime
    input数组)来计算选定输入的误差幅度
  • 用相同值填充的两个数组。这样,您就知道结果了(它应该与第一对相同)。
    • 试着找出一对导致较大舍入误差的数字(如1/10、0.1/1、0.2/2,它们最终都是0.1,不能用double;)正确表示)
  • 创建包含随机方差的输入数组(即+-1%*rand()。随着输入阵列的增长,这些应该是平衡的
比较结果时,使用
assertEquals(double,double,double)
,其中前两个是要比较的值,最后一个是精度(
1e-3
,逗号后3位)

最后,您需要使用该算法并查看其行为。当您发现问题时,请为此特定案例添加一个测试案例。

是的,您应该这样做。测试(应)始终涉及边界值


您可以提供一个epsilon边界,您可以断言该边界的答案(大致)是正确的。

我也同意您关于测试边界的一般陈述。然而,这确实是我这里的问题的一部分,我不确定“参数数量”是否可以在这里被视为“边界情况”…您可以使用新的double[Integer.MAX_VALUE]初始化参数。这是一个非常好的建议(没有想到这样的边界)。然而,我如何知道超过20亿个值和权重的正确加权平均值是什么,这样我就可以在测试中检查它了?你可以按升序填充数组:1.0 2.0。。。Integer.MAX_值,可以计算(Integer.MAX_值*(Integer.MAX_值+1)/2)/Integer.MAX_值。但要避免整数扭曲,请改用BigInteger。也许Wolfram Alpha会帮助您进行此计算;)谢谢,我要试试这些。