Страницы: Пред. 1 2
RSS
Варианты округления в VBA, Поиск самого быстрого
 
Цитата
sokol92: С момента изобретения формата Double
и снова — ссылка классная (спасибо-сохранил), но не понимаю, к чему она, если у меня в тестовом стенде вариативные переменные и сравниваются уже округленные значения (с различием во втором, а не десятом знаке)
А насчёт сравнения равенством я в курсе - по ссылке дополнительно закрепил  :idea:

Я это всё к чему: считается (см. скрин), что банковское округление Round более точно к искомой сумме членов без округления (и мой тест это также подтверждает с точностью на 7 сотых выше при 10 тысячах чисел), но при этом отклонение от математического округления идёт только в одну сторону (видно, если раскомментировать) — как так?))
Впрочем, это у же не совсем по теме…
Скрин с сайта
Скрин результатов теста
Тестовый стенд №2
Изменено: Jack Famous - 19.04.2020 14:11:23
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
Jack Famous написал:
отклонение от математического округления идёт только в одну сторону
Да, конечно, ведь математическое округление до целых, например, всегда округляет положительные числа с дробной долей 1/2 вверх, а банковское - то вверх, то вниз. Поэтому, в Налоговом Кодексе РФ прописано математическое округление до целых рублей при расчете налогов и до целых копеек - страховых взносов. :D  
Изменено: sokol92 - 19.04.2020 14:10:07
Владимир
 
Цитата
sokol92: математическое …  вверх, а банковское - то вверх, то вниз
тупанул - теперь понятно, почему. Спасибо)
Но ведь вопрос ещё есть - можно ли получить математическое округление с помощью VBA.Round, при этом быстрее, чем --Format$()?  :)

Цитата
sokol92: Поэтому, в Налоговом Кодексе РФ прописано математическое округление
не понял, из чего это следует (откуда "поэтому"), но думаю, что им так просто понятнее/проще  :D
Изменено: Jack Famous - 19.04.2020 14:15:47
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Добрый день, Алексей!
Налоговые службы всех стран мира стремятся к увеличению сборов налогов: тут и рубль пригодится, и копеечка... :)

Пару лет назад мы с Владимиром (ZVI) в переписке обсуждали вопрос создания "родной" функции VBA для математического округления.
Можно попробовать такую:
Код
' Математическое округление Number до NumDigitsAfterDecimal (от 0 до 12) знаков
Function RoundM(ByVal Number, Optional ByVal NumDigitsAfterDecimal As Long)
  Static aDiff(0 To 12) As Double, d As Double, d2 As Double, res, i As Long
  Const EPS As Double = 0.00000000000001 ' 1e-14
  If aDiff(0) = 0 Then
    aDiff(0) = 0.5
    For i = 1 To UBound(aDiff)
     aDiff(i) = 0.5 * 10 ^ (-i)
    Next i
  End If
  
  If Number < 0 Then
    RoundM = -RoundM(-Number)
    Exit Function
  End If
 
  res = VBA.Round(Number, NumDigitsAfterDecimal)
  
  If NumDigitsAfterDecimal <= 12 Then
    d = aDiff(NumDigitsAfterDecimal)
    d2 = Number - res - d
    If d2 >= -EPS And d2 <= EPS Then
      res = res + d + d
    End If
  End If
  
  RoundM = res
End Function
Дополненный тест для сравнения:

Код
Option Explicit
 
#If VBA7 Then
    Private Declare PtrSafe Function getFrequency Lib "kernel32" Alias _
        "QueryPerformanceFrequency" (cyFrequency As Currency) As Long
    Private Declare PtrSafe Function getTickCount Lib "kernel32" Alias _
         "QueryPerformanceCounter" (cyTickCount As Currency) As Long
#Else
    Private Declare Function getFrequency Lib "kernel32" Alias _
    "QueryPerformanceFrequency" (cyFrequency As Currency) As Long
    Private Declare Function getTickCount Lib "kernel32" Alias _
        "QueryPerformanceCounter" (cyTickCount As Currency) As Long
#End If
 
Function MicroTimer() As Double
' Returns seconds.
    Dim cyTicks1 As Currency
    Static cyFrequency As Currency
    '
    MicroTimer = 0
' Get frequency.
    If cyFrequency = 0 Then getFrequency cyFrequency
' Get ticks.
    getTickCount cyTicks1
' Seconds
    If cyFrequency Then MicroTimer = cyTicks1 / cyFrequency
End Function
 
Sub Test()
  Dim t As Double, nMax As Long, i As Long, d As Double
  nMax = 1000000
  
  ' Сначала без замера времени проверяем совпадение результатов участников
  For i = 1 To nMax
    d = WorksheetFunction.Round(1 + i / nMax, 2)
    If Abs(d - RoundM(1 + i / nMax, 2)) >= 0.00000000000001 Then Stop
    If Abs(d - CDbl(Format(1 + i / nMax, "0.00"))) >= 0.00000000000001 Then Stop
  Next i
    
  t = MicroTimer
  With WorksheetFunction
    For i = 1 To nMax
      d = .Round(1 + i / nMax, 2)
    Next i
  End With
   
  Debug.Print "WorksheetFunction.Round", FormatNumber(MicroTimer - t, 3)
   
  t = MicroTimer
  For i = 1 To nMax
     d = Round(1 + i / nMax + 0.00000001, 2)
  Next i
  Debug.Print "VBA.Round" & Space(10), FormatNumber(MicroTimer - t, 3)
   
  t = MicroTimer
  For i = 1 To nMax
     d = RoundM(1 + i / nMax, 2)
  Next i
  Debug.Print "RoundM" & Space(10), FormatNumber(MicroTimer - t, 3)
   
  t = MicroTimer
  For i = 1 To nMax
     d = CDbl(Format(1 + i / nMax, "0.00"))
  Next i
  Debug.Print "VBA.Format" & Space(10), FormatNumber(MicroTimer - t, 3)
   
End Sub

Если заменить в функции RoundM текст "RoundM" на "Round", то после добавления новой функции в любой проект банковское округление при обращении к Round превратится в математическое.
Изменено: sokol92 - 21.04.2020 13:47:44
Владимир
 
sokol92, приветствую! Спасибо за вариант!
Сегодня завал по работе - потом гляну и отвечу, а пока - вот ссылка на  интересную статью по округлению:
Изменено: Jack Famous - 21.04.2020 15:04:52
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Забыл поделиться: Стабильный и очень быстрый вариант от ZVI
Изменено: Jack Famous - 02.03.2021 10:32:11
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Страницы: Пред. 1 2
Наверх