CLng () и CDbl () сбрасывают десятичные дроби

Если вы хотите, чтобы значения в результирующем List<Value> находились в порядке ввода ключа Map<Key, Value>, вам нужно как-то «пройти через» SortedMap.

Либо запустите с конкретной реализацией SortedMap (например TreeMap) или вставьте ваш вход Map в SortedMap, прежде чем преобразовать его в List. например:

Map<Key,Value> map;
List<Value> list = new ArrayList<Value>( new TreeMap<Key Value>( map ));

В противном случае вы получите любой нативный порядок, предоставляемый реализацией Map, который часто может быть чем-то иным, чем упорядочение естественного ключа (Try Hashtable или ConcurrentHashMap, для разнообразия ).

4
задан Comintern 28 February 2019 в 02:17
поделиться

2 ответа

Все функции преобразования чисел в 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 напрямую с жестко заданным идентификатором локали. Например, чтобы получить Double из локализованной строки в США:

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
0
ответ дан Comintern 28 February 2019 в 02:17
поделиться

Двумя наиболее распространенными десятичными разделителями являются "." (США, Великобритания ...) и "," (Италия, Франция ...). Вы можете легко получить доступ к этому свойству из VBA, используя Application.DecimalSeparator (возвращая разделитель ОС).

Правильное решение состояло бы не в том, чтобы "22.14" трактовалось как String, а как действительное число, так что такая проблема интернационализации была разрешена для операционной системы.

Если вы действительно не можете иметь этот номер иначе, чем String (например, вы получаете эти значения из англо-американского API, который отправляет вам десериализованные числа), тогда Вы можете использовать аналогичную функцию:

Function InternationalCLng(ByVal st As String)

    Dim EnglishSep As Boolean: EnglishSep = ("." = Application.DecimalSeparator)
    If Not EnglishSep Then
        st = Replace(st, ".", Application.DecimalSeparator)
    End If
    InternationalCLng = CLng(st)

End Function

... которая заменяет . на правую DecimalSeparator и использовать вместо нее InternationalCLng("22.14"). Но, честно говоря, это взломать.

0
ответ дан Matteo NNZ 28 February 2019 в 02:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: