3

以下测试将失败C#

Assert.AreEqual<double>(10.0d, 16.1d - 6.1d);

问题似乎是一个浮点错误。

16.1d - 6.1d == 10.000000000000002

这让我在为使用double. 有没有办法来解决这个问题?

4

4 回答 4

5

十进制系统和双精度的二进制表示之间没有精确的转换(请参阅下面@PatriciaShanahan 的精彩评论,了解原因)。

在这种情况下,数字的 .1 部分是问题所在,它不能被有限地表示为双精度数(比如 1/3 不能被有限地精确地表示为十进制数)。

一个代码片段来解释发生了什么:

double larger = 16.1d; //Assign closest double representation of 16.1.
double smaller = 6.1; //Assign closest double representation of 6.1.
double diff = larger - smaller; //Assign closest diff between larger and  
                                //smaller, but since a smaller value has a  
                                //larger precision the result will have better  
                                //precision than larger but worse than smaller. 
                                //The difference shows up as the ...000002.

在比较双打时,请始终使用带参数的Assert.Equal重载。delta

或者,如果您确实需要精确的十进制转换,请使用decimal具有另一种二进制表示形式的数据类型,并且将10在您的示例中完全返回。

于 2013-08-18T15:35:45.950 回答
2

浮点数是基于指数对实际值的估计,因此测试正确失败。如果您需要两个十进制数的精确等价,您可能需要检查十进制数据类型。

于 2013-08-18T15:37:53.913 回答
1

如果您使用的是 NUnit,请使用该Within选项。您可以在这里找到更多信息:http ://www.nunit.org/index.php?p=equalConstraint&r=2.6.2 。

于 2013-08-18T15:38:04.423 回答
0

我同意安德斯·阿贝尔的观点。没有办法使用浮点数表示来做到这一点。
IEE 1985-754的直接结果中,只有可以表示的数字可以
总和中 2 的幂
精确存储和计算(只要选择的位数允许)。

例如:
1024 * 1.75 * 183.375 / 1040.0675 <-- 将精确存储
10 / 1.1 <-- 不会精确存储

如果您对有理数的精确表示几乎不感兴趣,您可以使用分数编写自己的数字实现。
这可以通过保存分子、分母和符号来完成。然后需要实现乘法、减法等操作(很难确保良好的性能)。toString() 方法可能看起来像这样(我假设 cachedRepresentation、cachedDotIndex 和 cachedNumerator 是成员变量)

 public String getString(int digits) {
            if(this.cachedRepresentation == ""){
                this.cachedRepresentation += this.positiveSign ? "" : "-";  
                this.cachedRepresentation += this.numerator/this.denominator; 
                this.cachedNumerator = 10 * (this.numerator % this.denominator);
                this.cachedDotIndex = this.cachedRepresentation.Length;
                this.cachedRepresentation += ".";
            }

            if ((this.cachedDotIndex + digits) < this.cachedRepresentation.Length)
                return this.cachedRepresentation.Substring(0, this.cachedDotIndex + digits + 1);

            while((this.cachedDotIndex + digits) >= this.cachedRepresentation.Length){
                this.cachedRepresentation += this.cachedNumerator / this.denominator;
                this.cachedNumerator = 10 * (this.cachedNumerator % denominator);
            }
            return cachedRepresentation;
        }


这对我有用。在具有长数字的操作本身上,我遇到了一些数据类型太小的问题(通常我不使用 c#)。我认为对于一个有经验的 c# 开发人员来说,在没有小数据类型问题的情况下实现这一点应该没有问题。

如果你想实现这一点,你应该在初始化和操作之前使用 euclids 最大通用除法器对分数进行缩小。

非有理数可以(在我知道的每种情况下)由一种算法指定,该算法尽可能接近您想要的精确表示(并且计算机允许)。

于 2014-02-27T22:04:15.527 回答