当您阅读MSDN on System.Single
时:
Single
符合IEC 60559:1989(IEEE 754)二进制标准 浮点运算。
和C#语言规范:
使用32位单精度表示
float
和double
类型 和64位双精度IEEE 754格式[...]
以后:
根据IEEE 754算法的规则计算产品。
您很容易得到float
类型及其乘法符合IEEE 754的印象。
IEEE 754的一部分 multiplcation明确定义。我的意思是,当你有两个float
个实例时,只存在一个float
using System;
static class Program
{
static void Main()
{
Console.WriteLine("Environment");
Console.WriteLine(Environment.Is64BitOperatingSystem);
Console.WriteLine(Environment.Is64BitProcess);
bool isDebug = false;
#if DEBUG
isDebug = true;
#endif
Console.WriteLine(isDebug);
Console.WriteLine();
float a, b, product, whole;
Console.WriteLine("case .58");
a = 0.58f;
b = 100f;
product = a * b;
whole = 58f;
Console.WriteLine(whole == product);
Console.WriteLine((a * b) == product);
Console.WriteLine((float)(a * b) == product);
Console.WriteLine((int)(a * b));
}
}
这是他们的“正确”产品。不允许产品依赖于计算系统的某些“状态”或“设置”。
现在,请考虑以下简单程序:
float
通过写一些关于环境的信息并编译配置,该程序只考虑两个a
(即b
和float
)及其产品。最后四个写行是有趣的。这是在使用调试x86 (左),发布x86 (中)和 x64 进行编译后在64位计算机上运行此输出的输出(右):
我们得出结论,简单"case .58"
操作的结果取决于构建配置。
float
之后的第一行是对两个float
的相等性的简单检查。我们希望它独立于构建模式,但事实并非如此。接下来的两行我们希望是相同的,因为它不会改变任何内容以将float
转换为"True↩ True"
。但他们不是。我们还希望他们阅读a*b
,因为我们正在将产品0.58
与自身进行比较。我们希望输出的最后一行与构建配置无关,但事实并非如此。
要弄清楚正确的产品是什么,我们手动计算。 a
(0 . 1(001 0100 0111 1010 1110 0)(001 0100 0111 1010 1110 0)...
)的二进制表示形式为:
0 . 1(001 0100 0111 1010 1110 0)(001 (*)
括号中的块是永远重复的句号。此数字的单精度表示需要四舍五入为:
Single
我们将舍入(在本例中为向下舍入)舍入为最接近的可表示b
。现在,数字“一百”(110 0100 . (**)
)是:
(*)
二进制。计算数字(**)
和 11 1001 . 1111 1111 1111 1111 1110 0100
的完整产品会给出:
11 1010 . 0000 0000 0000 0000 00
舍入(在本例中为四舍五入)为单精度给出
1
我们向上舍入,因为下一位是0
,而不是58f
(舍入到最近)。因此我们得出结论,根据IEEE,结果是0.59f * 100f
。这不是以任何方式给出的先验,例如59f
小于0.60f * 100f
,而60f
大于float
,根据IEEE
所以看起来代码的x64版本是正确的(上图中最右边的输出窗口)。
注意:如果这个问题的任何读者都有一个旧的32位CPU,那么听听上述程序的输出在他们的架构上会很有趣。
现在问题是:
float
乘法,然后“忘记”再次摆脱该精度?float
表达式转换为(a*b)
类型可以改变任何内容?float
拉出到一个临时局部变量,当它们应该在数学上(根据IEEE)等效时,改变行为?如果运行时选择以“人工”额外(64位)精度保持{{1}},程序员如何才能提前知道?(这是在.NET Framework的4.0版本中完成的。)
答案 0 :(得分:8)
我没有检查过你的算术,但我以前肯定看过类似的结果。除了调试模式有所不同之外,分配局部变量和实例变量也会产生影响。根据C#4规范的第4.1.6节,这是合法的:
可以以比操作的结果类型更高的精度执行浮点运算。例如,某些硬件体系结构支持“扩展”或“长双”浮点类型,其范围和精度高于
double
类型,并使用此更高精度类型隐式执行所有浮点运算。只有在性能成本过高的情况下,才能使这种硬件架构以 less 精度执行浮点运算。 C#允许更高精度的类型用于所有浮点运算,而不是要求实现放弃性能和精度。除了提供更精确的结果外,这几乎没有任何可衡量的影响。 [...]
我不能肯定地说这是不是在这里发生了什么,但我不会感到惊讶。