Як я можу правильно порівняти подвійні значення рівності в одиничному тесті?


20

Нещодавно я розробив модуль часових рядів, де мій часовий ряд по суті є SortedDictionnary<DateTime, double>.

Тепер я хотів би створити одиничні тести, щоб переконатися, що цей модуль працює завжди і дає очікуваний результат.

Поширеною операцією є обчислення продуктивності між точками у часовому ряду.

Тож я хочу створити часовий ряд із, скажімо, {1.0, 2.0, 4.0} (на деякі дати), і я очікую, що результат буде {100%, 100%}.

Річ у тім, що якщо я вручну створюю часовий ряд зі значеннями {1.0, 1.0} і перевіряю рівність (порівнюючи кожну точку), тест не пройшов би, оскільки завжди будуть неточності при роботі з бінарними уявленнями реальних числа.

Отже, я вирішив створити таку функцію:

private static bool isCloseEnough(double expected, double actual, double tolerance=0.002)
{
    return squaredDifference(expected, actual) < Math.Pow(tolerance,2);
}

Чи існує ще один поширений спосіб вирішення такої справи?

Відповіді:


10

Я можу придумати два інші способи вирішити цю проблему:

Ви можете використовувати Is.InRange:

Assert.That(result, Is.InRange(expected-tolerance, expected+tolerance));

Ви можете використовувати Math.Round:

Assert.That(Math.Round(result, sigDigits), Is.EqualTo(expected));

Я думаю, що обидва способи є більш виразними, ніж виділена функція, оскільки читач може точно побачити, що відбувається з вашим номером, перш ніж він отримає порівняно з очікуваним значенням.


2
Лише зауважте, що ця відповідь є специфічною для NUnit і демонструє модель твердження "на основі обмежень". Класична модель твердження виглядала б так: Assert.AreEqual (очікувана, фактична, толерантність);
RichardM

1
@RichardM: Опублікуйте це як відповідь, і я виберу це, прийняти його.
SRKX

Відповідь @dasblinkenlight правильна, просто додавши деякі деталі (оскільки це може бути не зрозуміло - класична модель твердження також NUnit). Інші тестові рамки (не MSTest), ймовірно, мають власну модель затвердження для роботи зі значеннями з плаваючою комою.
RichardM

1
Assert.That(result, Is.InRange(expected-tolerance, expected+tolerance));не вдасться, якщо tolerance/abs(expected) < 1E-16.
Quant_dev

@quant_dev Ви абсолютно праві. Оскільки ОП говорить про обчислення прибутку у відсотках, я припускав, що abs(expected)це буде одинарний або двозначний. Я також припускав допуск у районі 1E-9. Згідно з цими припущеннями, цей, безумовно, спрощений підхід міг би служити вам досить добре (я використовую Is.InRangeв своїх тестах).
dasblinkenlight


3

Це залежить від того, що ви робите з числами. Якщо ви тестуєте метод, який, наприклад, повинен вибрати відповідне значення з набору вхідних даних на основі деяких критеріїв, вам слід перевірити на сувору рівність. Якщо ви робите розрахунки з плаваючою комою, зазвичай вам потрібно буде протестувати з ненульовим допуском. Наскільки велика толерантність залежить від розрахунків, але з подвійною точністю хорошим початковим моментом є вибір відносної допуску 1E-14 для простих обчислень та 1E-8 (допуску) для більш складних. YMMV звичайно, і вам потрібно додати невеликий абсолютний допуск, якщо очікуваний результат дорівнює 0.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.