CDec:带小数的参数字符串的国际语法

CDec: international syntax for argument string with decimals

我的 Excel 所有内容(公式、界面、指南)都设置为英文。 我的 Windows 设置为用“,”表示小数点符号,用“.”表示。对于区域设置中的数字分组符号,因为我住在意大利。

如果我写这段代码:

    Dim v As Variant
    v = CDec("12345678901234567000,123456789")
    v = v + 50

结果在 Locals window 中显示为“12345678901234567050,123456789”(Variant/Decimal)。如果我 msgbox 也一样。

如果我用“.”而不是“,”,结果是 12345678901234567000123456839.

在VBA中,当写数字(以数字形式,而不是字符串形式)时,我必须使用英文语法,即“.”小数点符号。

我相信我的示例代码会 运行 错误地被 Windows 使用英语区域设置。

我怎样才能(将其修改为)使其 运行 正确地适应任何区域设置?

C* 转换函数(CIntCLngCStr 等)都设计用于在计算机的当前区域设置中工作。他们将使用当前的小数点分隔符,因此您假设 CDec 将无法在具有不同小数点的系统上正确处理硬编码 , 是正确的。

相反,StrVal始终使用英文分隔符,但不支持Decimal


所以想到的一个选择是获取小数点 at runtime:

Dim v As Variant
v = CDec("12345678901234567000" & Application.International(xlDecimalSeparator) & "123456789")

不过需要注意的是,如果Application.UseSystemSeparatorsFalse,而Application.DecimalSeparator已经改了,那么Application.International(xlDecimalSeparator)会变成return that changed separator,而不是那个来的从计算机语言环境。所以如果你不能保证 UseSystemSeparatorsTrue.

就不要使用这个方法

另一种选择是用十的幂除的形式来表示小数位,这与精确的fixed-point Decimal数据类型相匹配:

Dim v As Variant
v = CDec("12345678901234567000123456789") / CDec("1000000000")

另一种选择是自定义 "CDec" 在特定语言环境中明确工作,并始终在该语言环境中对字符串进行硬编码:

Option Explicit

#If VBA7 Then
Private Declare PtrSafe Function VarDecFromStr Lib "OleAut32.dll" (ByVal strIn As LongPtr, ByVal lcid As Long, ByVal dwFlags As Long, ByRef pdecOut As Variant) As Long
#Else
Private Declare Function VarDecFromStr Lib "OleAut32.dll" (ByVal strIn As Long, ByVal lcid As Long, ByVal dwFlags As Long, ByRef pdecOut As Variant) As Long
#End If

Private Const LOCALE_INVARIANT As Long = &H7F&
Private Const S_OK As Long = &H0

Public Function ParseDecimalFromEnUsString(ByVal s As String) As Variant
  Dim hr As Long

  hr = VarDecFromStr(StrPtr(s), LOCALE_INVARIANT, 0, ParseDecimalFromEnUsString)

  If hr <> S_OK Then
    Err.Raise 5, , "Cannot parse the string. Error " & Hex$(hr)
  End If
End Function
? ParseDecimalFromEnUsString("12345678901234567000.123456789")
12345678901234567000,123456789

? TypeName(ParseDecimalFromEnUsString("12345678901234567000.123456789"))
Decimal

(对于此代码的一个版本,可以更好地控制此答案的 what the string is allowed to contain, see revision 3。接收 NUMPRS_STD 的参数是要更改的参数。)