不幸的是,可以执行舍入的 VBA 的本机函数要么缺失、受限、不准确或有问题,而且每个函数只处理一种舍入方法。好处是它们速度很快,在某些情况下这可能很重要。
但是,精度通常是强制性的,并且以当今计算机的速度,几乎不会注意到稍微慢一点的处理,尤其是对于单个值的处理。以下链接中的所有函数的运行时间约为 1 µs。
完整的函数集 - 适用于所有常见的舍入方法、VBA 的所有数据类型、适用于任何值以及不返回意外值 - 可在此处找到:
Rounding values up, down, by 4/5, or to significant figures (EE)
或这里:
Rounding values up, down, by 4/5, or to significant figures (CodePlex)
代码仅在 GitHub:
VBA.Round
它们涵盖了正常的舍入方法:
向下舍入,可选择将负值向零舍入
四舍五入,可选择将负值从零舍入
四舍五入,从零或偶数(银行家四舍五入)
四舍五入到有效数字计数
前三个函数接受所有数字数据类型,而最后一个函数存在三种类型 - 分别用于 Currency、Decimal 和 Double。
它们都接受指定的小数位数 - 包括将舍入到数十、数百等的负数。返回类型为 Variant 的那些将返回 Null 用于难以理解的输入
还包括一个用于测试和验证的测试模块。
这里有一个示例 - 用于常见的 4/5 舍入。请研究嵌入式 cmets 以了解细微的细节以及使用 CDec 来避免位错误的方式。
' Common constants.
'
Public Const Base10 As Double = 10
' Rounds Value by 4/5 with count of decimals as specified with parameter NumDigitsAfterDecimals.
'
' Rounds to integer if NumDigitsAfterDecimals is zero.
'
' Rounds correctly Value until max/min value limited by a Scaling of 10
' raised to the power of (the number of decimals).
'
' Uses CDec() for correcting bit errors of reals.
'
' Execution time is about 1µs.
'
Public Function RoundMid( _
ByVal Value As Variant, _
Optional ByVal NumDigitsAfterDecimals As Long, _
Optional ByVal MidwayRoundingToEven As Boolean) _
As Variant
Dim Scaling As Variant
Dim Half As Variant
Dim ScaledValue As Variant
Dim ReturnValue As Variant
' Only round if Value is numeric and ReturnValue can be different from zero.
If Not IsNumeric(Value) Then
' Nothing to do.
ReturnValue = Null
ElseIf Value = 0 Then
' Nothing to round.
' Return Value as is.
ReturnValue = Value
Else
Scaling = CDec(Base10 ^ NumDigitsAfterDecimals)
If Scaling = 0 Then
' A very large value for Digits has minimized scaling.
' Return Value as is.
ReturnValue = Value
ElseIf MidwayRoundingToEven Then
' Banker's rounding.
If Scaling = 1 Then
ReturnValue = Round(Value)
Else
' First try with conversion to Decimal to avoid bit errors for some reals like 32.675.
' Very large values for NumDigitsAfterDecimals can cause an out-of-range error
' when dividing.
On Error Resume Next
ScaledValue = Round(CDec(Value) * Scaling)
ReturnValue = ScaledValue / Scaling
If Err.Number <> 0 Then
' Decimal overflow.
' Round Value without conversion to Decimal.
ReturnValue = Round(Value * Scaling) / Scaling
End If
End If
Else
' Standard 4/5 rounding.
' Very large values for NumDigitsAfterDecimals can cause an out-of-range error
' when dividing.
On Error Resume Next
Half = CDec(0.5)
If Value > 0 Then
ScaledValue = Int(CDec(Value) * Scaling + Half)
Else
ScaledValue = -Int(-CDec(Value) * Scaling + Half)
End If
ReturnValue = ScaledValue / Scaling
If Err.Number <> 0 Then
' Decimal overflow.
' Round Value without conversion to Decimal.
Half = CDbl(0.5)
If Value > 0 Then
ScaledValue = Int(Value * Scaling + Half)
Else
ScaledValue = -Int(-Value * Scaling + Half)
End If
ReturnValue = ScaledValue / Scaling
End If
End If
If Err.Number <> 0 Then
' Rounding failed because values are near one of the boundaries of type Double.
' Return value as is.
ReturnValue = Value
End If
End If
RoundMid = ReturnValue
End Function