Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Почему этот модульный тест не работает при сравнении двух двойников?

Автор вопроса: XaisoftОтветов: 4

У меня есть следующий код в vb.net, который вычисляет сумму до применения налога:

Public Shared Function CalculateRateBeforeTax(ByVal rate As Decimal, ByVal tax As Decimal) As Decimal
    Dim base As Decimal = rate / (1 + (tax / 100.0))
    Return Math.Round(base,2)
End Function

Вот некоторые сценарии, которые я настроил:

Ставка = 107, Налог = 7%, База = 100

Ставка = 325, налог = 6,5%, база = 305,16.

Ставка = 215, Налог = 125%, База = 95,55

Я поместил приведенные выше сценарии в некоторые модульные тесты, используя С# и среду тестирования nunit. Первый сценарий проходит, но другой терпит неудачу, и я не уверен, как я могу его пройти. Вот мои тесты:

[TestFixture]
class TaxTests
{
    [Test]
    public void CalculateRateBeforeTax_ShouldReturn100_WhenRateIs107AndTaxIs7Percent()
    {
        decimal expected = 100.0m;
        decimal actual = TaxUtil.CalculateRateBeforeTax(107.0m, 7.0m);

        Assert.AreEqual(expected,actual);
    }

    [Test]
    public void CalculateRateBeforeTax_ShouldReturn305point16_WhenRateIs325AndTaxIs6point5Percent()
    {
        decimal expected = 305.16m;
        decimal actual = TaxUtil.CalculateRateBeforeTax(325.0m, 6.5m);

        Assert.AreEqual(expected, actual);
    }

    [Test]
    public void CalculateRateBeforeTax_ShouldReturn95point55_WhenRateIs215AndTaxIs125Percent()
    {
        decimal expected = 95.55m;
        decimal actual = TaxUtil.CalculateRateBeforeTax(215.0m, 125.0m);

        Assert.AreEqual(expected, actual);
    }

}

Как я уже говорил, первый тест проходит, но результаты других тестов таковы:

Второй тест ожидался 305.1600000000000003d Но был: 305.1643192488263d

Третий тест ожидался 95,54999999999997 Но был: 95,55555555555555557d

  • Для денежных вы должны действительно использовать десятичный HadleyHope 30.03.2011
  • Что должен знать каждый программист об арифметике с плавающей запятой Oded 30.03.2011

nunit

Ответы

Ответ 1 · рейтинг 7 · Meta-Knight

Просто возьмите калькулятор и введите следующее: 325 / (1 + (6,5 / 100,0))

Результат 305.164319...

Затем вы спрашиваете, равно ли 305.164319... 305.16. Тест явно провален, это не те числа.

Теперь, если вам интересно, почему у вас немного другие числа, например 305.16000000000000003 вместо 305.16, это связано с некоторой потерей точности с типом Double. Вы можете использовать тип Decimal для большей точности.

Но самая важная проблема заключается в том, что значение, возвращаемое функцией CalculateRateBeforeTax, не усекается должным образом, чтобы иметь точность до цента. Вам просто нужно обрезать два десятичных знака следующим образом:

Dim rounded As Decimal = Math.Floor(base * 100) / 100

Теперь, изменив тип Double на тип Decimal, ваш Assert должен работать.

Ответ 2 · рейтинг 2 · Justin Niessner

Поздравляем. Ваши модульные тесты действительно сделали то, что должны были сделать, и обнаружили ошибку в коде, который вы тестируете.

У вас есть ошибки округления. К сожалению, это вызвано кодом VB.NET, который вы пытаетесь выполнить модульным тестом, а не кодом в ваших реальных тестах.

Вам нужно использовать тип данных с большей точностью. Я бы предложил заменить использование Double на Decimal.

Ответ 3 · Darren Young

Вы не можете гарантировать, что числа с плавающей запятой совпадают друг с другом, даже если они выглядят так.

Это зависит от ряда факторов, таких как процессор, архитектура и т. д. Как сказал Джастин, используйте десятичную дробь, если требуется точность.

Взгляните на отличный пост в блоге Джона Скитса: http://csharpindepth.com/Articles/General/FloatingPoint.aspx

Ответ 4 · CodesInChaos

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

Наблюдаемая проблема вызвана логическими ошибками в вашем коде округления. Вам нужно посмотреть, как правильно округлять такие значения. Это вопрос законов, а не математики.

Еще одна странность заключается в том, что ваш CalculateRateBeforeTax округляет возвращаемое значение до целочисленного значения, но опубликованные вами значения, похоже, рассчитываются без этого округления.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы