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

C# 如何在不添加额外数字的情况下将float转换为double?,c#,.net,type-conversion,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.900000000000,可以表示为一个double,这一点可以通过单元测试得到证明: double d2 = -99.9; Asser

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

float f = -99.9f;
double d = (double)f;
Assert.AreEqual(-99.9, d);
我知道在不同的地方增加了32位。然而,如果我直接赋值,我所追求的值,
-99.900000000000
,可以表示为一个double,这一点可以通过单元测试得到证明:

double d2 = -99.9;
Assert.AreEqual(-99.9, d2);
Assert.AreEqual(-99.9000000000000, d2);
因此,我的最终问题是:是否有可能将
-99.9f
转换为
双精度
,从而使其真正等于
-99.900000000000

编辑

我找到了一种解决方法,目前似乎适合我的特定应用(即,我不知道提前将浮点四舍五入到多少位精度):

我不确定这是否适用于所有情况;欢迎评论

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

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

请注意,如果我在delta值上再加一个零,测试将失败

您看到的问题是因为浮点分辨率

浮点数的精度较低(取决于您的需要),即使您指定-99.9f,浮点数也不会有该值,它实际上会有-99.9000015258789f作为值,因为当您将其转换为双精度时,您会看到该值,所以问题不是转换为双精度,而是使用浮点数

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


干杯。

问题是99.9不能在
中准确表示(对应于C#关键字
浮点

这里的问题是.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
Assert.AreEqual(-99.9, d2, 0.000001);
最终,无论有多少位,你都会得到略低于0.9或略高于0.9的结果,因为0.9不能用这样的位来精确表示,但这就是浮点数的存储方式

通过调用
.ToString()
,或仅通过
控制台.WriteLine
,或
“”+f
,甚至在调试器中检查,您看到的屏幕表示都可能被舍入,这意味着您将看不到存储的确切数字,而只看到舍入/格式化如何返回它

这就是为什么在比较浮点值时,您应该始终使用“epsilon”比较来进行比较

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

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

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

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

这就是你写比较的方式:

if (Math.Abs(actual - expected) < 0.000001) { ... }
此外,如果您想了解更多信息,本文有很多血淋淋的细节:


我不确定这是否可以解释。似乎
float
double
都可以表示
-99.9
——一个简单的控制台应用程序可以显示它。只有当从
float
转换到
double
时,问题才出现。不,我错了。
float
double
都不能正确表示
-99.9
。使用
.ToString(“G20”)
我分别得到
-99.9000015
-99.90000000000006
。是的,计算机中表示的十进制数通常都是不精确的,因为存储的位数越多,精度就越高,但它不会是完全相同的数。@Gusman,您能否给出具体示例,说明“但对于某些值,它也会失败,这取决于您有多少小数位”,这些失败案例是否适用于我的解决方法(请参见问题编辑)?不,这是不可能的,因为这两种表示法中的值都没有准确表示。它在屏幕上显示为99.9并不意味着存储在内存中的值正好代表99.9。在编写测试浮点计算输出的单元测试时,您应该始终考虑舍入误差,并在编写测试时说“如果实际值与预期值足够接近,我们就可以了”,而不是说“如果实际值与预期值完全相同,我们就可以了”因为后者很少是真的。在“clos”之前,它到底能走多远
Assert.AreEqual(-99.9, d2, 0.000001);