首页 > 解决方案 > CLng() 和 CDbl() 正在舍弃小数

问题描述

如果我CLng("22.14")在我的 PC 的 Excel VBA 编辑器中运行,我会得到预期的结果,22. 如果我在一些国际同事的 PC 上执行此操作,它会产生2214.

CDbl正在做类似的事情。我得到22.14,他们得到2214

我的想法:我假设一些设置定义了十进制字符(我的正确的是“。”而他们的则是别的东西)。我的搜索没有产生任何其他想法或解决方案。

标签: excelvbainternationalizationtype-conversion

解决方案


VBA 中的所有数字转换函数都支持区域设置,因此它们将忽略千位分隔符和货币符号。该IsNumeric函数的行为方式相同:

Public Sub Example()
    'en-US locale
    Debug.Print IsNumeric("$1,1,1,1,1,")    'True
    Debug.Print CLng("$1,1,1,1,1,")         '11111
End Sub

我知道处理这个问题的唯一与主机无关的解决方法(除了不将数字存储为String数据)是完全绕过内置的 VBA 转换并直接使用硬调用oleaut32.dll 中的底层转换函数-编码的区域设置 ID。例如,Double要从 en-US 本地化字符串中获取 a:

Public Declare PtrSafe Function VarR8FromStr Lib "oleaut32" _
    (ByVal strIn As LongPtr, ByVal lcid As Long, ByVal dwFlags As Long, ByRef pdblOut As Double) As Long

Public Const EN_US As Long = 1033

Public Function DoubleFromEnUsString(converting As String) As Double
    Dim converted As Double, result As Long
    result = VarR8FromStr(StrPtr(converting), EN_US, 0, converted)
    If (result = 0) Then
        DoubleFromEnUsString = converted
    Else
        Err.Raise 5
    End If
End Function

...或一个Long

Public Declare PtrSafe Function VarI4FromStr Lib "oleaut32" _
    (ByVal strIn As LongPtr, ByVal lcid As Long, ByVal dwFlags As Long, ByRef plOut As Long) As Long

Public Const EN_US As Long = 1033

Public Function LongFromEnUsString(converting As String) As Long
    Dim converted As Long, result As Long
    result = VarI4FromStr(StrPtr(converting), EN_US, 0, converted)
    If (result = 0) Then
        LongFromEnUsString = converted
    Else
        Err.Raise 5
    End If
End Function

示例用法:

Public Sub Sample()
    Debug.Print LongFromEnUsString("12.34")     '12
    Debug.Print DoubleFromEnUsString("12.34")   '12.34
End Sub

推荐阅读