免责声明:我知道 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 位整数类型。
- 根据the VBA specification 、CLng对Long进行Let-强制转换,数字类型之间的Let-强制使用Banker的舍入。 The same is true对于 VB.NET 的 CInt。
0.025
在这两种情况下都是 double IEEE 浮点常量。
因此,浮点乘法运算符或整数转换运算符的一些实现细节发生了变化。但是,出于与旧版 VBA 系统兼容性的原因,我需要在 .NET 应用程序中复制 VBA 的数学行为(无论它可能有多么错误)。
有什么办法可以做到这一点吗?有人编写了 Microsoft.VBA.Math 库吗?或者是否在某处记录了精确 VBA 算法,以便我可以自己完成?
最佳答案
VBA 和 VB.NET 的行为不同,因为 VBA 使用 80-bit "extended" precision用于中间浮点计算(即使 Double
是 64 位类型),而 VB.NET 始终使用 64 位精度。使用 80 位精度时,0.025 * 100 的值略大于 2.5,因此 CLng(0.025 * 100)
向上舍入为 3。
不幸的是,VB.NET 似乎不提供 80 位精度的算术。作为解决方法,您可以使用 Visual C++ 创建 native 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
关于.net - 在 .NET 中模拟 VBA 算术,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19498346/