Dot-Net
在 .NET 中模擬 VBA 算法
免責聲明:我知道 0.025 不能在 IEEE 浮點變數中精確表示,因此,舍入可能不會返回預期的結果。那不是我的問題!
是否可以在 .NET 中模擬 VBA 算術運算符的行為?
例如,在 VBA 中,以下表達式產生
3:Dim myInt32 As Long myInt32 = CLng(0.025 * 100) ' yields 3但是,在 VB.NET 中,以下表達式產生
2:Dim myInt32 As Integer myInt32 = CInt(0.025 * 100) ' yields 2根據規範,兩者都應該返回相同的值:
- Long (VBA) 和 Integer (VB.NET) 是 32 位整數類型。
- 根據VBA 規範,CLng 對 Long 進行 Let-coercion,數字類型之間的 Let-coercion 使用 Banker 的捨入。VB.NET 的 CInt也是如此。
0.025在這兩種情況下都是雙精度 IEEE 浮點常數。因此,浮點乘法運算符或整數轉換運算符的某些實現細節發生了變化。但是,出於與舊版 VBA 系統兼容的原因,我需要在 .NET 應用程序中複製 VBA 的數學行為(無論它可能是錯誤的)。
有沒有辦法做到這一點?有人寫了一個
Microsoft.VBA.Math庫嗎?或者是否在某處記錄了精確的VBA 算法,以便我自己做?
VBA 和 VB.NET 的行為不同,因為 VBA 使用80 位“擴展”精度進行中間浮點計算(即使
Double是 64 位類型),而 VB.NET 始終使用 64 位精度。使用 80 位精度時,0.025 * 100 的值略大於 2.5,因此CLng(0.025 * 100)向上取整為 3。不幸的是,VB.NET 似乎不提供 80 位精度的算術。作為一種解決方法,您可以使用 Visual C++ 創建本機 Win32 DLL 並通過 P/Invoke 呼叫它。例如:
#include <cmath> #include <float.h> #pragma comment(linker, "/EXPORT:MultiplyAndRound=_MultiplyAndRound@16") extern "C" __int64 __stdcall MultiplyAndRound(double x, double y) { unsigned int cw = _controlfp(0, 0); _controlfp(_PC_64, _MCW_PC); // use 80-bit precision (64-bit significand) double result = floor(x * y + 0.5); if (result - (x * y + 0.5) == 0 && fmod(result, 2)) result -= 1.0; // round down to even if halfway between even and odd _controlfp(cw, _MCW_PC); // restore original precision return (__int64)result; }在 VB.NET 中:
Declare Function MultiplyAndRound Lib "FPLib.dll" (ByVal x As Double, ByVal y As Double) As Long Console.WriteLine(MultiplyAndRound(2.5, 1)) ' 2 Console.WriteLine(MultiplyAndRound(0.25, 10)) ' 2 Console.WriteLine(MultiplyAndRound(0.025, 100)) ' 3 Console.WriteLine(MultiplyAndRound(0.0025, 1000)) ' 3