c# - Release模式即使对于浮点变量也使用 double

标签 c# .net cross-platform floating-point-precision

我的算法正在计算 epsilon用于单精度 浮点运算。它应该是 1.1921e-007 左右的东西。这是代码:

static void Main(string[] args) {
    // start with some small magic number
    float a = 0.000000000000000013877787807814457f;
    for (; ; ) {
        // add the small a to 1
        float temp = 1f + a;
        // break, if a + 1 really is > '1'
        if (temp - 1f != 0f) break;
        // otherwise a is too small -> increase it
        a *= 2f;
        Console.Out.WriteLine("current increment: " + a); 
    }
    Console.Out.WriteLine("Found epsilon: " + a); 
    Console.ReadKey(); 
}

在 Debug模式下,它给出了以下合理的输出(缩写):

current increment: 2,775558E-17
current increment: 5,551115E-17
...
current increment: 2,980232E-08
current increment: 5,960464E-08
current increment: 1,192093E-07
Found epsilon: 1,192093E-07

但是,当切换到release模式时(无论with/Without优化!),代码给出如下结果:

current increment: 2,775558E-17
current increment: 5,551115E-17
current increment: 1,110223E-16
current increment: 2,220446E-16
Found epsilon: 2,220446E-16

对应于 double 的值。所以我假设,一些优化会导致对 double 值进行计算。当然在这种情况下结果是错误的!

此外:只有在项目选项中针对 X86 发布时才会发生这种情况。再次重申:优化开/关无关紧要。我在 64 位 WIN7、VS 2010 Ultimate 上,目标是 .NET 4.0。

什么可能导致这种行为?一些WOW问题?如何以可靠的方式绕过它?如何防止 CLR 生成使用 double 而不是单精度计算的代码?

注意:切换到“Any CPU”甚至“X64”作为平台目标是没有选择的——即使这里没有出现问题。但是我们有一些 native 库,针对 32/64 位有不同的版本。所以目标一定要具体。

最佳答案

正如评论中所讨论的,这是预期的。可以通过移除 JIT 将值保存在寄存器中(这将比实际值更宽)的能力来回避 - 通过将其强制到一个字段(具有明确定义的大小):

class WorkingContext
{ 
    public float Value; // you'll excuse me a public field here, I trust
    public override string ToString()
    {
        return Value.ToString();
    }
}
static void Main()
{
    // start with some small magic number
    WorkingContext a = new WorkingContext(), temp = new WorkingContext();
    a.Value = 0.000000000000000013877787807814457f;
    for (; ; )
    {
        // add the small a to 1
        temp.Value = 1f + a.Value;
        // break, if a + 1 really is > '1'
        if (temp.Value - 1f != 0f) break;
        // otherwise a is too small -> increase it
        a.Value *= 2f;
        Console.Out.WriteLine("current increment: " + a);
    }
    Console.Out.WriteLine("Found epsilon: " + a);
    Console.ReadKey();
}

有趣的是,我先用一个结构尝试了这个,但是 JIT 能够看穿我的作弊行为(大概是因为它都在堆栈上)。

关于c# - Release模式即使对于浮点变量也使用 double ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6277623/

相关文章:

c# - C# 4.0 的 ExpandoObject 是否支持基于原型(prototype)的继承?

c# - wcf msmq 监听器服务删除消息但不处理

c++ - 在 Visual Studio 2010 下制作基于 GCC 的项目构建和运行

c++ - 是否有像包含目录别名这样的概念?

c# - resharper "cleanup code"与 'var' 关键字

c# - 有没有办法为 C# 库设置调用应用程序执行权限?

c# - 将 List<T> 转换为 List<Interface>

c# - 在编译时不知道类型的情况下使用 Windsor 解析泛型接口(interface)?

c# - 与更传统的语言相比,用 F# 编写什么样的应用程序会受益?

c - 替换纯 C 中的链接器部分