简体   繁体   English

为什么在比较两个双打时这个单元测试失败了?

[英]Why does this unit test fail when comparing two doubles?

I have the following code in vb.net that calculates the amount before tax was applied: 我在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

Some scenarios I setup were: 我设置的一些场景是:

Rate = 107, Tax = 7%, Base = 100 费率= 107,税= 7%,基数= 100

Rate = 325, Tax = 6.5%, Base = 305.16 费率= 325,税= 6.5%,基数= 305.16

Rate = 215, Tax = 125%, Base = 95.55 费率= 215,税= 125%,基数= 95.55

I put the above scenarios into some unit tests using c# and using the nunit testing framework. 我使用c#和使用nunit测试框架将上述场景放入一些单元测试中。 The first scenario passes, but the other fails and I am not sure how I can get it to pass. 第一个场景通过,但另一个失败,我不知道如何让它通过。 Here is are my tests: 这是我的测试:

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

}

As I said before, the first test passes, but the results of the other tests are: 正如我之前所说,第一次测试通过,但其他测试的结果是:

Second Test expected 305.1600000000000003d But was: 305.1643192488263d 第二次测试预计305.1600000000000003d但是:305.1643192488263d

Third Test expected 95.54999999999997 But was: 95.55555555555555557d 第三次测试预计95.54999999999997但是:95.55555555555555557d

Just take out a calculator and enter the following: 325 / (1 + (6.5 / 100.0)) 只需拿出一个计算器并输入以下内容:325 /(1 +(6.5 / 100.0))

The result is 305.164319... 结果是305.164319 ...

Then you're asking if 305.164319... is equal to 305.16. 然后你问305.164319 ......是否等于305.16。 The test obviously fails, they are not the same numbers. 测试显然失败了,他们的数字不一样。

Now if you're wondering why you have slightly different numbers than this, like 305.1600000000000003 instead of 305.16, this is because there is some loss of precision with Double type. 现在,如果你想知道为什么你的数字略有不同,比如305.1600000000000003而不是305.16,这是因为Double类型有一些精度损失。 You can use the Decimal type for more precision. 您可以使用Decimal类型以获得更高的精度。

But the most important problem is that the value returned by CalculateRateBeforeTax is not truncated correctly to have precision up to the cent. 但最重要的问题是CalculateRateBeforeTax返回的值没有被正确截断,以达到精确度。 You just have to truncate two decimals like this: 你只需截断两个小数,如下所示:

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

Now by changing Double type by Decimal type your Assert should work. 现在通过改变Double类型的Decimal类型,您的Assert应该可以工作。

Congratulations. 恭喜。 Your Unit Tests have actually done what they are supposed to do and found a bug with the code you are testing. 您的单元测试实际上已经完成了他们应该做的事情,并且发现了您正在测试的代码的错误。

You have rounding errors. 你有舍入错误。 Unfortunately this is being caused by the VB.NET code you are trying to Unit Test rather than the code in your actual tests. 不幸的是,这是由您尝试单元测试的VB.NET代码而不是实际测试中的代码引起的。

You need to use a data type with more precision. 您需要使用更精确的数据类型。 I would suggest replacing your use of Double with Decimal . 我建议替换你使用Double with Decimal

You cannot guarantee that floating point numbers are the same as each other, even when they appear so. 您无法保证浮点数彼此相同,即使它们看起来如此。

It depends on a number of factors such as processor, architecture, etc. As Justin has said, use decimal if precision is required. 它取决于许多因素,如处理器,体系结构等。正如Justin所说,如果需要精度,请使用decimal。

Have a look at Jon Skeets excellent blog post: http://csharpindepth.com/Articles/General/FloatingPoint.aspx 看看Jon Skeets优秀博客文章: http//csharpindepth.com/Articles/General/FloatingPoint.aspx

While the other posters are right stating that you should use Decimal instead of Double that isn't the cause of your observed problem. 虽然其他海报是正确的说明你应该使用Decimal而不是Double ,这不是你观察到的问题的原因。

The observed problem is caused by logical mistakes in your rounding code. 观察到的问题是由舍入代码中的逻辑错误引起的。 You need to look up how to correctly round such values. 您需要查找如何正确舍入此类值。 This is a question of laws, and not math. 这是一个法律问题,而不是数学问题。

Another strange thing is that your CalculateRateBeforeTax rounds its return value to an integral value, but the values you posted seem to be calculated without that rounding. 另一个奇怪的事情是你的CalculateRateBeforeTax其返回值四舍五入为一个整数值,但是你发布的值似乎是在没有舍入的情况下计算的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM