Hei*_*nzi 15 .net vb.net math vba
免责声明:我知道0.025无法在IEEE浮点变量中准确表示,因此,舍入可能无法返回预期的值.那不是我的问题!
是否可以在.NET中模拟VBA算术运算符的行为?
例如,在VBA中,以下表达式产生3:
Dim myInt32 As Long
myInt32 = CLng(0.025 * 100) ' yields 3
Run Code Online (Sandbox Code Playgroud)
但是,在VB.NET中,以下表达式产生2:
Dim myInt32 As Integer
myInt32 = CInt(0.025 * 100) ' yields 2
Run Code Online (Sandbox Code Playgroud)
根据规范,两者都应返回相同的值:
0.025 在两种情况下都是双精度IEEE浮点常数.因此,浮点乘法运算符或整数转换运算符的一些实现细节发生了变化.但是,出于与传统VBA系统兼容的原因,我需要在.NET应用程序中复制VBA的数学行为(不管它可能是错误的).
有办法做到这一点吗?有人写过Microsoft.VBA.Math图书馆吗?或者是在某处记录了精确的 VBA算法,所以我自己可以做到这一点?
Mic*_*Liu 15
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;
}
Run Code Online (Sandbox Code Playgroud)
在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
Run Code Online (Sandbox Code Playgroud)
鉴于VBA应该使用Banker的舍入,乍一看我似乎很清楚这个bug实际上是在VBA方面.银行家在中点(.5)四舍五入,因此结果数字是偶数.因此,要进行正确的Banker舍入,2.5 应该舍入到2,而不是 3.这与.Net结果匹配,而不是VBA结果.
但是,根据从当前删除的答案中提取的信息,我们也可以在VBA中看到此结果:
Dim myInt32 As Integer
myInt32 = CInt(2.5) ' 2
myInt32 = CInt(0.025 * 100) ' 3
Run Code Online (Sandbox Code Playgroud)
这使得看起来VBA中的舍入是正确的,但乘法运算产生的结果大大超过2.5.由于我们不再处于中间位置,因此银行家的规则不适用,我们最多可以达到3.
因此,要解决这个问题,你需要弄清楚什么是VBA代码确实与乘法指令做.无论记录的是什么,观察结果证明VBA以不同于.Net的方式处理这一部分.一旦你弄明白到底发生了什么,幸运的是你将能够模拟这种行为.
一种可能的选择是返回浮点数的旧备用数据库:检查你是否处于中点的一个小的delta中,如果是,则只使用中点.这是一些(未经测试的)天真的代码:
Dim result As Double = 0.025 * 100
Dim delta As Double = Double.Epsilon
Dim floor As Integer = Math.Floor(result)
If Math.Abs(result - (CDbl(floor) + 0.5)) <= delta Then
result = floor + 0.5
End
Run Code Online (Sandbox Code Playgroud)
我强调未经测试,因为在这一点上,我们已经对小计算机舍入错误的结果感到奇怪.在这种情况下天真的实施可能不够好.至少,您可能希望为delta使用3或4个因子.此外,你可以从这段代码中得到的最好的结果是它可以强制VBA匹配.Net,当你真正追求的是反向时.