2012-03-06 114 views
1

我有一個C#代碼,當「優化代碼」選項關閉時工作良好,但在其他情況下會失敗。是否有任何函數或類屬性可以阻止函數或類的優化,但讓編譯器優化其他函數或類?C#編譯器「優化代碼」:僅在代碼片段上禁用

(我試過不安全或MethodImpl,但沒有成功)

感謝

編輯: 我做了一些更多的測試... 的代碼是這樣的:

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

當a = 1且d = 0時,arg應該爲0. 該代碼是一個由Excel通過ExcelDNA調用的函數。

調用從一個優化的控制檯應用程序相同的代碼:確定

調用從Excel這個代碼,無需優化:行

從Excel調用此代碼與優化:不正常,則arg == 0爲假(而不是0)

與被調用函數之前的[MethodImpl(MethodImplOptions.NoOptimization)]相同的結果。

+1

你是什麼意思「失敗」?你是否遇到異常,是否表現緩慢或其他?代碼的功能在優化時不應改變。 – Botz3000 2012-03-06 17:26:22

+6

如果啓用優化時失敗,則可能是錯誤的...請發佈代碼! – 2012-03-06 17:31:28

+0

如果你已經試過'MethodImpl.NoOptimization'它仍然失敗,我懷疑你的問題是優化(我會懷疑這將是無論如何),這是別的東西。發佈一些代碼。 – 2012-03-06 17:50:16

回答

2

這就是你在處理浮點數據類型時得到的結果。由於雙精度有限,並不是每個值都可以表示,有時這些微小的精度誤差會加起來,所以您不會精確到0,而是非常接近的值。您需要預期(檢查值是否接近足夠爲0)。

+0

我不認爲使用'decimal'或'int'和'Math.Atan2'是合理的。使用'decimal'不會神奇地給你無限的精度,它只是讓你精確地表示小數部分。 – svick 2012-03-06 18:25:03

+0

我什麼時候說小數會提供無限精度?這一切都取決於他想用它做什麼。結果是一個相當小的值,你不能對不精確性做任何事情。比較像0這樣的double值和constant值通常會導致這樣的錯誤。 – Botz3000 2012-03-06 18:35:36

+0

是的,使用'double'會導致這樣的錯誤。但是如何使用'decimal'幫助? – svick 2012-03-06 19:12:02

2

這很可能與Excel可能已設置的floating point mode有關 - 這意味着您的程序計算的浮點數因承載程序集(DLL)的程序(Excel)而略有不同。這可能會影響計算結果的方式,或者如何/什麼值被自動強制爲零。

要絕對確保你不會碰到不同的浮點模式和/或錯誤的問題,你應該檢查的平等,而通過檢查值非常接近。 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。

+0

「這很可能與Excel可能設置的浮點模式有關」=>感謝鏈接,我會檢查。 – Poca 2012-03-07 08:42:42

+1

@Poca請記住,如果你將它改回到你期望的狀態(我不確定什麼是默認設置),你可能會把Excel弄糟 - 所以最好做一下「親密度檢查」。 – 2012-03-07 09:31:25