简体   繁体   English

C#编译器“优化代码”:仅禁用代码片段

[英]C# Compiler “Optimize code” : disable on a code fragment only

I have a C# code which is working good when the "optimize code" option is off, but fails otherwise. 我有一个C#代码,当“优化代码”选项关闭时工作正常,但是否则失败。 Is there any function or class attribute which can prevent the optimisation of a function or class, but let the compiler optimize the others ? 是否有任何函数或类属性可以阻止函数或类的优化,但让编译器优化其他函数或类?

(I tried unsafe or MethodImpl, but without success) (我尝试过不安全或MethodImpl,但没有成功)

Thanks 谢谢

Edit : I have done some more test... The code is like this : 编辑:我做了一些测试...代码是这样的:

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

With a = 1 and d = 0, arg should be 0. Thid code is a function which is called by Excel via ExcelDNA. 如果a = 1且d = 0,则arg应为0.该代码是Excel通过ExcelDNA调用的函数。

Calling an identical code from an optimized console app : OK 从优化的控制台应用程序调用相同的代码:好的

Calling this code from Excel without optimization : OK 在没有优化的情况下从Excel调用此代码:好的

Calling this code from Excel with optimization : Not OK, arg == 0 is false (instead arg is a very small value near 0, but not 0) 通过优化从Excel调用此代码:不行,arg == 0为false(而arg是一个非常小的值,接近0,但不是0)

Same result with [MethodImpl(MethodImplOptions.NoOptimization)] before the called function. 与被调用函数之前的[MethodImpl(MethodImplOptions.NoOptimization)]相同的结果。

That is what you get when working with floating point datatypes. 这是使用浮点数据类型时得到的结果。 You don't get exactly 0, but a very close value, since a double has limited precision and not every value can be represented and sometimes those tiny precision errors add up. 你得到的确不是0,而是一个非常接近的值,因为double的精度有限,并不是每个值都可以表示,有时那些微小的精度误差会加起来。 You either need to expect that (check that the value is close enough to 0). 您要么需要(检查该值是否足够接近0)。

This is very likely to do with the floating point mode which Excel likely has set - meaning that your program is calculating floating points slightly different because of the program (Excel) hosting your assembly (DLL). 这很可能与Excel可能设置的浮点模式有关 - 这意味着由于程序(Excel)托管程序集(DLL),程序计算的浮点略有不同。 This might impact how your results are calculated, or how/what values are automatically coerced to zero. 这可能会影响结果的计算方式,或者自动强制转换为零的方式/值。

To be absolutely sure you are not going to run into issues with different floating point modes and/or errors you should check for equality rather by checking if the values are very close together. 为了确保您不会遇到不同浮点模式和/或错误的问题,您应该检查是否相等,而不是通过检查值是否非常接近。 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.

I also ported the re-interpret integer comparison, in case you find that more suitable (it deals with larger values more consistently). 我还移植了重新解释整数比较,以防您发现更合适(它更一致地处理更大的值)。

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

Alternatively you could try and NGen the assembly and see if you can work around the either the mode Excel has, or how it is hosting the CLR. 或者,你可以尝试NGen组件,看看你是否可以解决Excel的模式,或者它如何托管CLR。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM