Компилятор C # «Оптимизировать код»: отключить только фрагмент кода - PullRequest
1 голос
/ 06 марта 2012

У меня есть код C #, который работает хорошо, когда опция «оптимизировать код» отключена, но в противном случае не работает. Есть ли какой-либо атрибут функции или класса, который может помешать оптимизации функции или класса, но позволить компилятору оптимизировать остальные?

(я пытался использовать unsafe или MethodImpl, но безуспешно)

Спасибо

Редактировать: Я сделал еще один тест ... Код такой:

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

При a = 1 и d = 0 arg должно быть 0. Код Thid - это функция, которая вызывается из Excel через ExcelDNA.

Вызов идентичного кода из оптимизированного консольного приложения: ОК

Вызов этого кода из Excel без оптимизации: ОК

Вызов этого кода из Excel с оптимизацией: не в порядке, arg == 0 ложно (вместо arg очень маленькое значение около 0, но не 0)

Тот же результат с [MethodImpl (MethodImplOptions.NoOptimization)] перед вызываемой функцией.

Ответы [ 2 ]

2 голосов
/ 06 марта 2012

Скорее всего, это связано с режимом с плавающей запятой , который, вероятно, установлен в Excel - это означает, что ваша программа вычисляет плавающие точки немного по-другому из-за программы (Excel), в которой размещается ваша сборка (DLL). Это может повлиять на то, как рассчитываются ваши результаты или как / какие значения автоматически приводятся к нулю.

Чтобы быть абсолютно уверенным, что вы не столкнетесь с проблемами с различными режимами с плавающей запятой и / или ошибками, вы должны проверить на равенство, а не проверять, находятся ли значения очень близко друг к другу. Это на самом деле не хак.

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.

2 голосов
/ 06 марта 2012

Это то, что вы получаете при работе с типами данных с плавающей запятой.Вы не получите точно 0, но очень близкое значение, поскольку двойное число имеет ограниченную точность, и не каждое значение может быть представлено, и иногда эти крошечные ошибки точности складываются.Вы также должны ожидать этого (убедитесь, что значение близко достаточно к 0).

...