如何在不添加额外数字的情况下将float转换为double?

时间:2015-10-22 03:19:24

标签: c# .net type-conversion

我遇到了一个有趣的问题:当我将float-99.9f转换为double变量时,该变量的值为-99.9000015258789 因此,此单元测试失败:

float f = -99.9f;
double d = (double)f;
Assert.AreEqual(-99.9, d);

我知道在不同的地方添加了额外的32位。然而,我所追求的价值-99.9000000000000,如果我直接指定它就表示为双精度,正如这个单位测试所证明的那样:

double d2 = -99.9;
Assert.AreEqual(-99.9, d2);
Assert.AreEqual(-99.9000000000000, d2);

所以我的最终问题是:是否可以将-99.9f转换为double,使其真正等于-99.9000000000000

修改

我找到了一个解决方法,暂时似乎对我的特定应用程序运行良好(即我不知道要将浮点数四舍五入到精确数字的位数):

float f = -99.9f;
double d = double.Parse(f.ToString("r"));
Assert.AreEqual(-99.9, d);

我不确定这是否适用于所有情况;欢迎提出意见

编辑2 根据Lasse V. Karlsen在下面的回答,解决方案是使用等效于AreEqual方法的MSTest:

Assert.AreEqual(-99.9, d, 0.00001);

请注意,如果我要向该delta值添加一个零,则测试将失败

2 个答案:

答案 0 :(得分:6)

问题在于99.9无法在SingleDouble中完全表示(对应于C#关键字floatdouble)。

这里的问题是.9必须写成两个幂的分数之和。因为底层表示是比特,我们只能为每个比特位置说它是开和关,并且分数侧的每个比特位置意味着1/2 ^ N,这意味着0.9可以表示如下:

1   1   1    1    1      1      1      1       1       1
- + - + - + -- + --- + ---- + ---- + ----- + ----- + ------ + ....
2   4   8   64   128   1024   2048   16384   32768   262144

最后,无论你有多少比特,你最终都会略微低于0.9或略高于0.9,因为0.9不能用这样的位来精确表示,但这就是浮点数的存储方式。

您看到的屏幕上的表示形式,无论是通过调用.ToString(),还是仅调用Console.WriteLine,还是"" + f,甚至是在调试器中进行检查,都可能会被舍入,这意味着你不会看到存储的确切数字,只能看到舍入/格式化的方式。

这就是为什么在比较浮点值时,你应该总是使用" epsilon"比较。

你的单元测试基本上说"只要实际值与预期值完全相同,我们就可以了#34;但是当处理浮点值时,这种情况很少发生。事实上,我会说它很少会发生,除了一件事,当你使用常量作为预期和实际并且它们属于同一类型时,它会立即发现它。

相反,您应该编写代码来测试实际值足够接近到期望值,其中"足够接近"与可能值的范围相比,它是非常小的,并且每种情况可能会有所不同。

您不应期望Single和Double可以表示完全相同的值,因此来回转换甚至可能最终返回不同的值。

另请注意,对于.NET,浮点计算在DEBUG和RELEASE构建方面略有不同,因为处理器的内置FPU部分通常可以比存储类型更高的精度运行,这意味着如果编译器/优化器/抖动最终使用FPU进行一系列计算,结果可能与值暂时存储在编译器生成的变量中时略有不同。

所以这就是你写比较的方式:

if (Math.Abs(actual - expected) < 0.000001) { ... }

其中0.000001是&#34;足够接近&#34;。

就许多单元测试框架而言,比如NUnit,您可以要求比较将此考虑在内:

Assert.AreEqual(-99.9, d2, 0.000001);

此外,如果您想了解更多相关信息,本文有很多细节:

答案 1 :(得分:3)

您看到的问题是浮动分辨率。

浮点数的精确度很低(取决于你的需求),即使你指定-99.9f,浮点数也没有那个值,它真的会有-99.9000015258789f作为值,因为当你将它转换为加倍时看到那个值,所以问题不是转换为double,而是使用float。

如果你知道你的数字会有多少小数位,你可以使用Math.Round(floatNumber,decimalPlaces),它几乎可以解决所有问题,但对于某些值,它也会失败,这取决于你有多少小数位。

干杯。