小编典典

将结果强制转换为float并返回float更改结果

c#

为什么False在.NET 4中打印此代码?似乎是显式强制转换导致某些意外行为。

除了“浮点数不准确”或“不要那样做”之外,我想要一个答案。

float a(float x, float y)
{
  return ( x * y );
}

float b(float x, float y)
{
  return (float)( x * y );
}

void Main()
{
  Console.WriteLine( a( 10f, 1f/10f ) == b( 10f, 1f/10f ) );
}

PS:此代码来自单元测试,而不是发布代码。该代码是故意以这种方式编写的。我怀疑它最终会失败,但是我想确切地知道何时以及为什么。答案证明了此技术的有效性,因为它提供了一种超出对浮点确定性的通常理解的理解。这就是以这种方式编写此代码的目的;刻意探索。

PPS:单元测试在.NET 3.5中通过,但是现在升级到.NET 4后失败。


阅读 343

收藏
2020-05-19

共1个答案

小编典典

大卫的评论是正确的,但不够有力。不能保证 在同一程序中两次 执行该计算会产生相同的结果。

在这一点上,C#规范非常清晰:


浮点运算可以比运算的结果类型更高的精度执行。例如,某些硬件体系结构支持比双精度类型具有更大范围和精度的“扩展”或“长双精度”浮点类型,并使用此更高精度类型隐式执行所有浮点运算。只有付出过高的性能代价,才能使此类硬件体系结构以较低的精度执行浮点运算,而不是要求实现同时牺牲性能和精度,而C#允许将高精度类型用于所有浮点运算。
。除了提供更精确的结果外,这几乎没有任何可测量的效果。但是,在以下形式的表达式中x * y / z,如果相乘产生的结果超出双精度范围,但是随后的除法将暂时结果带回双精度范围,那么以较高范围格式计算表达式的事实可能会导致产生有限结果,而不是无限


C#编译器,抖动和运行时都具有宽广的纬度,可以随时随地为您提供比规范要求 更准确的结果 -不需要选择一致地这样做,事实上,它们确实可以不。

如果您不喜欢这样做,请不要使用二进制浮点数。使用小数或任意精度有理数。

我不明白为什么在返回float的方法中强制转换为float会有所不同

优点。

您的示例程序演示了微小的变化如何导致较大的影响。您会注意到,在某些版本的运行时中,强制转换为float会得出与未执行的结果不同的结果。
当您显式转换为float时,C#编译器会提示运行时 说:“如果碰巧正在使用此优化,则将其退出超高精度模式”。如规范所指出的,
这有潜在的性能成本。

这样做会凑合到“正确答案”,这仅仅是一个偶然的意外;之所以能找到正确的答案,是因为在这种情况下, 精度的损失恰恰导致了在正确方向上的损失

.net 4有何不同?

您问3.5和4.0运行时之间有什么区别;区别很明显,在4.0中,在特定情况下,抖动选择精度更高,而3.5抖动则选择不精度。这并不意味着在3.5中是不可能的。在运行时的每个版本和C#编译器的每个版本中都可以实现。您碰巧遇到了一个案例,在您的计算机上,它们的详细信息不同。但是
始终 允许抖动来进行此优化,并且总是一时兴起。

当在编译时计算常量浮点数时,C#编译器完全有权选择进行类似的优化。根据编译器运行时状态的详细信息,常量中的两个看似相同的计算可能会得出不同的结果。

更普遍地,您对浮点数应该具有实数的代数性质的期望与现实完全不符。他们没有那些代数性质。浮点运算甚至没有 关联
;他们当然不遵守您所期望的乘法逆定律。浮点数仅是实数的近似值。一种近似值,足以模拟物理系统,计算摘要统计信息或类似的东西。

2020-05-19