2014-10-01 7 views
2

Я приведу упрощенный пример моей реальной ситуации:Java, BigDecimal: Как выполнить единичный тест для ошибок округления?

Предположим, что мне нужно реализовать код Java для вычисления Weighted arithmetic mean. Мне даны два массива значений с плавающей запятой (выраженные как удваивания), каждая из которых имеет одну и ту же длину, первая из которых содержит значения, а вторая - их соответствующие веса.

Давайте также сказать, что я сделать некоторые реализации, которая возвращает значение с плавающей точкой (также двойной), представляющую Weighted среднее арифметическое входных значений:

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

я написать модульный тест, проходящий несколько значений (например, 3 значения + 3 веса). Сначала я делаю ручной расчет их взвешенного арифметического значения (используя калькулятор), а затем записываю единичный тест, который проверяет, возвращает ли мой код это значение.

Я считаю, что такой тест не имеет отношения к ситуации, когда количество используемых значений существенно больше из-за ошибок округления. Возможно, код, который я реализовал, хорошо работает для 3 значений + 3 веса (для заданной точности), потому что ошибка округления меньше точности в этом случае, но очень возможно, что ошибка округления становится больше, чем желаемая точность для 1000 значения + 1000 весов.

Мой вопрос:

  • я должен также написать модульный тест, который проверяет для очень большого числа значений (а «наихудший сценарий» для использования в производстве)?
  • если надо, то как его написать? Как получить правильное значение, чтобы я мог использовать его в своих тестах. (Вычисление средств вручную для значений 2x1000 кажется вроде плохой идеей, даже если используется Weighted mean calculator ...)?
  • то же самое для подобных сценариев: вычисления geometric mean и т.д. ...

ответ

1

При написании юнит-тестов, вы всегда должны дать где-нибудь. Хитрость заключается в том, чтобы отказаться, когда вы уверены, что вы знаете достаточно :-)

В вашем случае, несколько простых тестов являются:

  • Пустые массивы
  • Создайте второй алгоритм, который использует точная арифметика (например, BigDecimal входных массивов) для расчета полей ошибок для выбранных входов
  • Два массива, которые заполнены теми же значениями. Таким образом, вы знаете результат (он должен быть таким же, как и первая пара).
    • попытаться найти пару чисел, которые вызывают большие ошибки округления (например, 1/10, 0,1/1, 0,2/2, что все в конечном итоге, как 0,1, которые не могут быть представлены надлежащим образом с использованием двойного; see here)
  • Создайте массивы ввода, содержащие случайные отклонения (т.е. + - 1% * rand()). Они должны выровняться по мере увеличения входных массивов.

При сравнении результатов, используйте assertEquals(double, double, double), где первые два значения для сравнения и последний из которых является точность (1e-3 3 цифр после запятой).

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

+0

спасибо, это замечательный ответ. –

1

Да, вы должны. Тестирование (обязательно) всегда включает граничные значения.

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

+0

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

+1

Вы можете инициализировать параметры с помощью нового double [Integer.MAX_VALUE]. – user

+0

Это очень хорошее предложение (не подумав об этой границе). Однако, как я могу узнать, какие правильные взвешенные средства имеют более 2 миллиардов значений и весов на самом деле, поэтому я могу проверить это в своем тесте? –