Skip to main content

[探索 3 分鐘] 檢查浮點數是否恰為整數, 效率型做法 (c#)

Question : 判斷一個浮點數輸入值, 是不是剛好是整數, 範例輸出入如下 (input → output)。
  • 11.112 → false
  • 11.000 → true 
就是這麼簡單。想到兩招, 一個用 Math 來計算, 如果無條件捨去跟無條件進位答案相同, 那肯定本身就是整數; 另一招是用安全的 TryParse 來判斷是否為 float。
public static bool IsIntegerByMath(float f)
{
    return Math.Ceiling(f) == Math.Floor(f); 
}

public static bool IsIntegerByParse(float f)
{
    int i;
    return int.TryParse(f.ToString(), out i);
}
如此一來, 程式會動了, 考試應該也有過。但若要追求簡潔或效能的極致, 還有以下的做法。
public static bool IsIntegerByMode(float f)
{
    return f % 1 == 0;
}

public static bool IsIntegerFloor(float f)
{
    return f == Math.Floor(f);
}
網路上的這兩個版本, 直接理解 C# 語言對於浮點數與整數的相互操作, 寫出更高效的寫法。而上面的執行效率各是如何 ? 未測先估 TryParse 是最慢的, 但最快的版本究竟快在哪裡 ? 跑個 1,000 萬次來試試。
答案揭曉, Math 類操作最快, 也就是上述 IsIntegerFloor() 版本; 扣除掉本來就會 BG 的 IsIntegerByParse() 版本, 雖然比剩餘兩個版本快, 但差異幾乎可以忽略。翻 Math.Floor 原始碼, 原來又是個的 native 函式。
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public static extern double Floor(double d); 

至於為何要用這種寫法呢, 網路上的 討論 頗多, 但參考 MSDN 的描述, 一言以蔽之, 就是「它會呼叫 CLR 內的方法」。
成員名稱描述
AggressiveInlining此方法應該盡可能內嵌。
ForwardRef方法已宣告,但是其實作在其他地方提供。
InternalCall呼叫是內部的,亦即,它會呼叫在 Common Language Runtime 內實作的方法。
The call is internal, that is, it calls a method that is implemented within the common language runtime.
NoInlining方法無法內嵌。 內嵌是一項最佳化作業,透過該作業,方法呼叫可使用方法主體取代。
NoOptimization對可能的程式碼產生問題進行偵錯時,方法不是由 Just-In-Time (JIT) 編譯器或原生程式碼產生最佳化 (請參閱 Ngen.exe (https://msdn.microsoft.com/zh-tw/library/6t9t5wcf(v=vs.110).aspx))。
PreserveSig方法簽章會完全依宣告方式匯出。
Synchronized方法一次只能由一個執行緒來執行。 靜態方法鎖定型別,而執行個體方法鎖定執行個體。 在任何執行個體函式中只能執行一個執行緒,且只有一個執行緒可在任何類別的靜態函式中執行。
Unmanaged方法是以 Unmanaged 程式碼實作。

參考資料

  • https://msdn.microsoft.com/zh-tw/library/system.runtime.compilerservices.methodimploptions(v=vs.110).aspx
  • https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/math.cs
  • https://stackoverflow.com/questions/142252/test-if-a-floating-point-number-is-an-integer
  • https://stackoverflow.com/questions/11161120/whats-the-point-of-methodimploptions-internalcall

Comments