Страницы: 1
RSS
Корректное сравнение вещественных чисел (VBA)
 
доброго времени суток всем.
наверное, я совсем заработался, не могу сейчас сообразить.
прошу помочь разобраться.

суть проблемы: известно, что вычисления с вещественными числами выполняются не абсолютно точно из-за особенностей машинной арифметики.
поэтому, к примеру, результат, на самом деле равный 5-ти, может быть представлен как 5.00000000000000, 5.00000000000001 или 4.99999999999998 - в зависимости от того, как он был получен.
мне нужно производить сравнения таких чисел, но корректно, т.е. с учетом этих самых особенностей.

на равенство проверяю так:
Код
const eps=0.0000001
if abs(a-b) < eps then ' равны


а как проверить на больше-меньше?
я додумался до такого:
Код
const eps=0.0000001
if a-b > -eps then ' a больше b
if a-b < eps then ' a меньше b


но сомневаюсь.

а с нестрогими условиями (>= и <=) совсем запутался :(

хелпми.
фрилансер Excel, VBA - контакты в профиле
"Совершенствоваться не обязательно. Выживание — дело добровольное." Э.Деминг
 
Код
If ABS(a-b)< eps Then
If a-b < eps Then ' a меньше b
If b-a < eps Then ' b меньше a

я использую всегда строго
Изменено: TheBestOfTheBest - 04.06.2015 21:32:05
Неизлечимых болезней нет, есть неизлечимые люди.
 
Саша, не совсем понял сути вопроса, но я вот набросал вот такой макрос (результаты - в комментариях):
Код
Sub CompareDigits()

    Dim num1 As Variant
    Dim num2 As Variant

    Dim str1 As String
    Dim str2 As String
    
    str1 = "4,99999999999998"
    str2 = "4,99999999999998"
    
    num1 = CDec(str1)
    num2 = CDec(str2)
    
    MsgBox "num1 = num2: " & num1 = num2 ' FALSE
    MsgBox "num1 = num2: " & (CStr(num1) = CStr(num2)) ' TRUE

End Sub
There is no knowledge that is not power
 
спасибо, разобрался.
код TheBestOfTheBest - тот же, что и у меня.
а проверка на нестрогое соответствие не имеет смысла.

Цитата
Johny написал: Саша, не совсем понял сути вопроса
ага  ;)
вопрос был в другом.
Изменено: ikki - 04.06.2015 22:22:45
фрилансер Excel, VBA - контакты в профиле
"Совершенствоваться не обязательно. Выживание — дело добровольное." Э.Деминг
 
По-моему, на больше-меньше надо проверять без eps: сравнение это вычитание, и оно дает вполне однозначный результат: больше нуля или меньше нуля. Если до этого проверили на равенство с точностью до eps.
Код
If Abs(a - b) < eps Then
  'a = b
ElseIf a < b Then
  'a меньше b
Else
  'b меньше a
End If
 
Цитата
Johny написал: MsgBox "num1 = num2: " & num1 = num2 ' FALSE
Евгений, приоритет конкатенации выше, поэтому корректнее так: MsgBox "num1 = num2: " & (num1 = num2) ' True
Изменено: ZVI - 05.06.2015 01:05:47
 
Цитата
ZVI написал:
Евгений, приоритет конкатенации выше
Владимир, точно! :) С "CStr(num1)" я правильно написал, а с просто num1... :D
There is no knowledge that is not power
 
Александр (ikki), если сравнивать с eps, то можно "пролететь", если числа в экспоненциальном формате.
Лучше сравнивать все 15 значащих разряда числа, отбросив остальные незначащие и учесть степенную часть после E .
Вот пример, описывающий проблемность использования eps, и в конце - как правильнее (но немного медленнее, конечно)
Код
Sub Test()

  Const eps = 0.0000001
  Dim a#, b#

  ' Число Pi
  a = 3.14159265358979      ' Большую точность явной константой задать не получится
  b = WorksheetFunction.Pi  ' А так точнее! Используйте в точных расчетах такой вариант

  ' здесь выдаст "равно"
  If Abs(a - b) < eps Then
    Debug.Print "равно, разность =" & CStr(a - b)
  Else
    Debug.Print "не равно, разность =" & CStr(a - b)
  End If

  ' Добавим степенную часть
  a = a * 1E+100
  b = b * 1E+100

  ' и здесь уже "не равно"
  If Abs(a - b) < eps Then
    Debug.Print "равно, разность = " & CStr(a - b)
  Else
    Debug.Print "не равно, разность = " & CStr(a - b)
  End If

  ' А вот так правильнее, CStr очищает число от "мусорных" разрядов
  a = CDbl(CStr(a))
  b = CDbl(CStr(b))
  Debug.Print Sgn(a - b)  ' 0 равно, 1 больше, -1 меньше

End Sub
Изменено: ZVI - 05.06.2015 02:13:51
 
Цитата
ZVI написал: если сравнивать с eps, то можно "пролететь", если числа в экспоненциальном формате
Владимир, спасибо.
об экспоненциальном я даже не вспомнил, ибо у меня числа от 0 до 1000
а вот скорость - важна, ибо таких проверок много.
фрилансер Excel, VBA - контакты в профиле
"Совершенствоваться не обязательно. Выживание — дело добровольное." Э.Деминг
 
Ну, тогда можно округлить числа с помощью функции round, несмотря на её банковскую сущность. И сравнить. Это тоже будет быстро.
Изменено: ZVI - 05.06.2015 02:04:01
 
недавно столкнулся с этим - решил применением currency формата
нужно было в ценах соблюдать точность до копейки.. с ценами все получилось, но перевод суммы на этот формат был ошибочным - так как сумма накапливалась пошагово, то в конце вылезала ошибка округления. так что сумму накапливал в дабл..
ну а так - раунд ее до нужного знака :)  и потом сравнивать
Живи и дай жить..
Страницы: 1
Наверх