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

标签 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,正如这个单元测试所证明的那样:

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

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

编辑

我找到了一种解决方法,目前,它似乎适用于我的特定应用程序(即我事先不知道要将 float 四舍五入到多少位精度的应用程序):

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

请注意,如果我向该增量值再添加一个零,测试将失败

最佳答案

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

这里的问题是 .9 必须写成 2 的分数的和。因为底层表示是位,我们只能对每个位的位置说它是开和关,小数边的每个位位置表示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 不能用这样的位精确表示,但这就是 float 的存储方式。

您看到的屏幕表示,通过调用 .ToString(),或只是 Console.WriteLine,或 ""+ f,甚至在调试器中检查,都可能被四舍五入,这意味着您将看不到存储的确切数字,只能看到四舍五入/格式化返回的方式。

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

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

相反,您应该编写代码来测试实际值是否足够接近期望值,其中“足够接近”是指与可能值的范围相比非常小的东西,并且很可能因人而异。

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

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

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

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

其中 0.000001 是“足够接近”。

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

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

另外,如果你想了解更多,这篇文章有很多血淋淋的细节:

关于c# - 如何在不添加额外数字的情况下将 float 转换为 double?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33272842/

相关文章:

c# - html编码空间的HtmlDecode不是空间

c# - 我什么时候应该写静态方法?

c# - 如何使用 GUI 在 Visual Studio 项目中添加和编译 Windows 窗体,而不是直接从 Visual Studio?

java - 如何在 Java 中声明一个 uint8_t 数组

vb.net - VB编译器从字符串到数字的转换

c# - 无法将 SelectedObjectCollection 转换为 ObjectCollection ?

c# - 在 c# wpf 中,我可以将 listview 列动态构建为类方法吗?

c# - 触发 OnPropertyChanged 时更新其他属性

.net - 带有 GUI 的开源 .NET Rich Text Editor UserControl 全部实现

c++ - 将变量作为函数参数传递时由于隐式转换导致精度损失