【问题标题】:C# Compiler "Optimize code" : disable on a code fragment onlyC# 编译器“优化代码”:仅在代码片段上禁用
【发布时间】:2012-03-06 17:23:32
【问题描述】:

我有一个 C# 代码,当“优化代码”选项关闭时运行良好,否则失败。是否有任何函数或类属性可以阻止函数或类的优化,而让编译器优化其他的?

(我尝试了 unsafe 或 MethodImpl,但没有成功)

谢谢

编辑: 我又做了一些测试... 代码是这样的:

double arg = (Math.PI / 2d - Math.Atan2(a, d)); 

当 a = 1 和 d = 0 时,arg 应该是 0。 该代码是Excel通过ExcelDNA调用的函数。

从优化的控制台应用程序调用相同的代码:好的

在没有优化的情况下从 Excel 调用此代码:好的

通过优化从 Excel 调用此代码:不好,arg == 0 为假(相反,arg 是接近 0 的一个非常小的值,但不是 0)

与调用函数前的 [MethodImpl(MethodImplOptions.NoOptimization)] 结果相同。

【问题讨论】:

  • “失败”是什么意思?您是否遇到异常,是执行缓慢还是其他原因?优化后代码的功能不应改变。
  • 如果启用优化后失败,那么可能是错误的......请发布代码!
  • 如果您尝试了MethodImpl.NoOptimization 并且仍然失败,我怀疑您的问题是优化(无论如何我都怀疑它会是),这是另外一回事。贴一些代码。
  • 尝试使用 decimal 而不是 double。
  • 我不认为使用小数是一个好主意,Excel传递的参数是双倍的,atan2的也是一样的。

标签: c# compiler-optimization


【解决方案1】:

这很可能与 Excel 可能设置的 floating point mode 有关 - 这意味着您的程序计算浮点数略有不同,因为程序 (Excel) 托管您的程序集 (DLL)。这可能会影响您的结果的计算方式,或者如何/哪些值被自动强制为零。

为了绝对确保您不会遇到不同浮点模式和/或错误的问题,您应该检查是否相等,而不是检查值是否非常接近。 This is not really a hack.

public class AlmostDoubleComparer : IComparer<double>
{
    public static readonly AlmostDoubleComparer Default = new AlmostDoubleComparer();
    public const double Epsilon = double.Epsilon * 64d; // 0.{322 zeroes}316

    public static bool IsZero(double x)
    {
        return Compare(x, 0) == 0;
    }

    public static int Compare(double x, double y)
    {
        // Very important that cmp(x, y) == cmp(y, x)
        if (Double.IsNaN(x) || Double.IsNaN(y))
            return 1;
        if (Double.IsInfinity(x) || Double.IsInfinity(y))
            return 1;

        var absX = Math.Abs(x);
        var absY = Math.Abs(y);
        var diff = absX > absY ? absX - absY : absY - absX;
        if (diff < Epsilon)
            return 0;
        if (x < y)
            return -1;
        else
            return 1;
    }

    int IComparer<double>.Compare(double x, double y)
    {
        return Compare(x, y);
    }
}

// E.g.
double arg = (Math.PI / 2d - Math.Atan2(a, d));
if (AlmostDoubleComparer.IsZero(arg))
   // Regard it as zero.

我还移植了重新解释整数比较,以防你发现它更合适(它更一致地处理更大的值)。

public class AlmostDoubleComparer : IComparer<double>
{
    public static readonly AlmostDoubleComparer Default = new AlmostDoubleComparer();
    public const double MaxUnitsInTheLastPlace = 3;

    public static bool IsZero(double x)
    {
        return Compare(x, 0) == 0;
    }

    public static int Compare(double x, double y)
    {
        // Very important that cmp(x, y) == cmp(y, x)
        if (Double.IsNaN(x) || Double.IsNaN(y))
            return 1;
        if (Double.IsInfinity(x) || Double.IsInfinity(y))
            return 1;

        var ix = DoubleInt64.Reinterpret(x);
        var iy = DoubleInt64.Reinterpret(y);
        var diff = Math.Abs(ix - iy);
        if (diff < MaxUnitsInTheLastPlace)
            return 0;

        if (ix < iy)
            return -1;
        else
            return 1;
    }

    int IComparer<double>.Compare(double x, double y)
    {
        return Compare(x, y);
    }
}

[StructLayout(LayoutKind.Explicit)]
public struct DoubleInt64
{
    [FieldOffset(0)]
    private double _double;
    [FieldOffset(0)]
    private long _int64;

    private DoubleInt64(long value)
    {
        _double = 0d;
        _int64 = value;
    }

    private DoubleInt64(double value)
    {
        _int64 = 0;
        _double = value;
    }

    public static double Reinterpret(long value)
    {
        return new DoubleInt64(value)._double;
    }

    public static long Reinterpret(double value)
    {
        return new DoubleInt64(value)._int64;
    }
}

或者,您可以尝试NGen 程序集,看看您是否可以解决 Excel 的模式,或者它如何托管 CLR。​​

【讨论】:

  • "这很可能与 Excel 可能设置的浮点模式有关" => 感谢您的链接,我会检查一下。
  • @Poca 请记住,如果您将其更改回您所期望的(我不确定默认值是什么),您可能会与 Excel 混淆 - 所以最好进行“接近性”检查。
【解决方案2】:

这就是您在使用浮点数据类型时得到的结果。你得到的不是精确的 0,而是一个非常接近的值,因为 double 的精度有限,并不是每个值都可以表示,有时这些微小的精度误差会加起来。您要么需要预料到这一点(检查该值是否接近 足够 到 0)。

【讨论】:

  • 我认为将decimalintMath.Atan2 一起使用没有意义。并且使用decimal 不会神奇地给你无限的精度,它只是让你准确地表示小数。
  • 我什么时候说过十进制可以提供无限精度?此外,这一切都取决于他想用它做什么。结果是一个很小的值,你不能对不精确做任何事情。将双精度值与 0 等常量值进行比较通常会导致此类错误。
  • 是的,使用double 会导致此类错误。但是使用decimal 有什么帮助呢?
  • 它可以最大限度地减少舍入误差,但你是对的,在这种情况下它可能无济于事。
  • "你得到的不是 0,而是一个非常接近的值" => 好的,但是为什么当从 Excel 中调用 optim 时,arg == 0 为 false,而当 optim 关闭时, arg == 0 对于相同的参数是否为真?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-11
  • 1970-01-01
  • 1970-01-01
  • 2013-03-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多