Как округлить в MS Access, VBA

каков наилучший способ округления в доступе VBA?

мой текущий метод использует метод Excel

Excel.WorksheetFunction.Round(...

но я ищу средство, которое не полагается на Excel.

12 ответов


будьте осторожны, функция VBA Round использует округление банкира, где она округляется .5 до четного числа, например:

Round (12.55, 1) would return 12.6 (rounds up) 
Round (12.65, 1) would return 12.6 (rounds down) 
Round (12.75, 1) would return 12.8 (rounds up)   

в то время как функция листа Excel круглый, всегда округляет .5 вверх.

Я сделал несколько тестов, и похоже .5 округление вверх (симметричное округление) также используется при форматировании ячеек, а также для округления ширины столбца (при использовании общего формата чисел). Флаг "точность как показано", похоже, не делает никакого округления, он просто использует округленный результат формата ячейки.

Я попытался реализовать функцию SymArith от Microsoft в VBA для моего округления, но обнаружил, что исправление имеет ошибку, когда вы пытаетесь дать ему число, подобное 58.55; функция дает результат 58.5 вместо 58.6. Затем я, наконец, обнаружил, что вы можете использовать круглую функцию листа Excel, например:

приложение.Круглый(58.55, 1)

Это позволит вам сделать нормальный округление в VBA, хотя это может быть не так быстро, как некоторые пользовательские функции. Я понимаю, что из этого вопроса вышел полный круг, но хотел включить его для полноты.


чтобы немного расширить принятый ответ:

" функция Round выполняет округление до четного, которое отличается от круглого до большего."
--Microsoft

формат всегда округляется.

  Debug.Print Round(19.955, 2)
  'Answer: 19.95

  Debug.Print Format(19.955, "#.00")
  'Answer: 19.96

ACC2000: ошибки округления при использовании чисел с плавающей запятой:http://support.microsoft.com/kb/210423

ACC2000: как округлить число вверх или вниз желаемым шагом: http://support.microsoft.com/kb/209996

Круглая Функция:http://msdn2.microsoft.com/en-us/library/se6f2zfx.aspx

Как Реализовать Пользовательские Процедуры Округления:http://support.microsoft.com/kb/196652


в Швейцарии и, в частности, в страховой отрасли, мы должны использовать несколько правил округления, в зависимости от того, если это чеш, выгода и т. д.

в настоящее время я использую функцию

Function roundit(value As Double, precision As Double) As Double
    roundit = Int(value / precision + 0.5) * precision
End Function

который, кажется, работает хорошо


Int и Fix являются полезными функциями округления, которые дают вам целочисленную часть числа.

Int всегда округляет - Int (3.5) = 3, Int(-3.5) = -4

Fix всегда округляет до нуля-Fix (3.5) = 3, Fix(-3.5) = -3

есть также функции принуждения, в частности CInt и CLng, которые пытаются принудить число к целочисленному типу или длинному типу (целые числа находятся между -32,768 и 32,767, длинные-между-2,147,483,648 и 2,147,483,647). Они оба будут округляться к ближайшему целому числу, округляясь от нуля от .5-CInt (3.5) = 4, Cint(3.49) = 3, CInt(-3.5) = -4 и т. д.


1 place = INT(number x 10 + .5)/10
3 places = INT(number x 1000 + .5)/1000

и так далее.Вы часто обнаружите, что, по-видимому, такие решения kludgy намного быстрее, чем использование функций Excel, потому что VBA, похоже, работает в другом пространстве памяти.

например If A > B Then MaxAB = A Else MaxAB = B примерно на 40 x быстрее, чем с помощью ExcelWorksheetFunction.Макс!--3-->


к сожалению, собственные функции VBA, которые могут выполнять округление, либо отсутствуют, ограничены, неточны или ошибочны, и каждый обращается только к одному методу округления. Плюс в том, что они быстрые, и это может быть важно в некоторых ситуациях.

однако часто точность является обязательной, и со скоростью компьютеров сегодня немного медленная обработка вряд ли будет замечена, действительно, не для обработки отдельных значений. Все функции в приведенных ниже ссылках выполняются по адресу около 1 мкс.

полный набор функций - для всех распространенных методов округления, всех типов данных VBA, для любого значения и не возвращающих неожиданных значений-можно найти здесь:

округление значений вверх, вниз, на 4/5 или до значимых цифр (EE)

или здесь:

округление значений вверх, вниз, на 4/5 или до значимых цифр (CodePlex)

код только в GitHub:

VBA.Раунд

они охватывают обычные методы округления:

  • округлить вниз, с возможностью округления отрицательных значений до нуля

  • округлить, с возможностью округления отрицательных значений от нуля

  • раунд на 4/5, либо от нуля или до четного (округление банкира)

  • округлить до числа значимых цифры

первые три функции принимают все числовые типы данных, в то время как последний существует в трех разновидностях - для валюты, десятичной и двойной соответственно.

все они принимают заданное количество десятичных знаков, включая отрицательное число, которое округляется до десятков, сотен и т. д. Те, у кого Variant как тип возврата, вернут Null для непонятного ввода

модуль теста Для теста и проверки включен как что ж.

пример здесь-для общего округления 4/5. Пожалуйста, изучите комментарии в строке для тонких деталей и пути CDEC и используется, чтобы избежать ошибок.

' Common constants.
'
Public Const Base10     As Double = 10

' Rounds Value by 4/5 with count of decimals as specified with parameter NumDigitsAfterDecimals.
'
' Rounds to integer if NumDigitsAfterDecimals is zero.
'
' Rounds correctly Value until max/min value limited by a Scaling of 10
' raised to the power of (the number of decimals).
'
' Uses CDec() for correcting bit errors of reals.
'
' Execution time is about 1µs.
'
Public Function RoundMid( _
    ByVal Value As Variant, _
    Optional ByVal NumDigitsAfterDecimals As Long, _
    Optional ByVal MidwayRoundingToEven As Boolean) _
    As Variant

    Dim Scaling     As Variant
    Dim Half        As Variant
    Dim ScaledValue As Variant
    Dim ReturnValue As Variant

    ' Only round if Value is numeric and ReturnValue can be different from zero.
    If Not IsNumeric(Value) Then
        ' Nothing to do.
        ReturnValue = Null
    ElseIf Value = 0 Then
        ' Nothing to round.
        ' Return Value as is.
        ReturnValue = Value
    Else
        Scaling = CDec(Base10 ^ NumDigitsAfterDecimals)

        If Scaling = 0 Then
            ' A very large value for Digits has minimized scaling.
            ' Return Value as is.
            ReturnValue = Value
        ElseIf MidwayRoundingToEven Then
            ' Banker's rounding.
            If Scaling = 1 Then
                ReturnValue = Round(Value)
            Else
                ' First try with conversion to Decimal to avoid bit errors for some reals like 32.675.
                ' Very large values for NumDigitsAfterDecimals can cause an out-of-range error 
                ' when dividing.
                On Error Resume Next
                ScaledValue = Round(CDec(Value) * Scaling)
                ReturnValue = ScaledValue / Scaling
                If Err.Number <> 0 Then
                    ' Decimal overflow.
                    ' Round Value without conversion to Decimal.
                    ReturnValue = Round(Value * Scaling) / Scaling
                End If
            End If
        Else
            ' Standard 4/5 rounding.
            ' Very large values for NumDigitsAfterDecimals can cause an out-of-range error 
            ' when dividing.
            On Error Resume Next
            Half = CDec(0.5)
            If Value > 0 Then
                ScaledValue = Int(CDec(Value) * Scaling + Half)
            Else
                ScaledValue = -Int(-CDec(Value) * Scaling + Half)
            End If
            ReturnValue = ScaledValue / Scaling
            If Err.Number <> 0 Then
                ' Decimal overflow.
                ' Round Value without conversion to Decimal.
                Half = CDbl(0.5)
                If Value > 0 Then
                    ScaledValue = Int(Value * Scaling + Half)
                Else
                    ScaledValue = -Int(-Value * Scaling + Half)
                End If
                ReturnValue = ScaledValue / Scaling
            End If
        End If
        If Err.Number <> 0 Then
            ' Rounding failed because values are near one of the boundaries of type Double.
            ' Return value as is.
            ReturnValue = Value
        End If
    End If

    RoundMid = ReturnValue

End Function

Если вы говорите о округлении до целочисленного значения (а не округлении до n десятичные знаки), всегда есть старый школьный способ:

return int(var + 0.5)

(вы можете сделать эту работу за n десятичные знаки тоже, но он начинает получать немного грязно)


Ланс уже упоминал наследование округления bug в реализации VBA. Поэтому мне нужна реальная функция округления в приложении VB6. Вот один, который я использую. Он основан на одном, который я нашел в интернете, как указано в комментариях.

' -----------------------------------------------------------------------------
' RoundPenny
'
' Description:
'    rounds currency amount to nearest penny
'
' Arguments:
'    strCurrency        - string representation of currency value
'
' Dependencies:
'
' Notes:
' based on RoundNear found here:
' http://advisor.com/doc/08884
'
' History:
' 04/14/2005 - WSR : created
'
Function RoundPenny(ByVal strCurrency As String) As Currency

         Dim mnyDollars    As Variant
         Dim decCents      As Variant
         Dim decRight      As Variant
         Dim lngDecPos     As Long

1        On Error GoTo RoundPenny_Error

         ' find decimal point
2        lngDecPos = InStr(1, strCurrency, ".")

         ' if there is a decimal point
3        If lngDecPos > 0 Then

            ' take everything before decimal as dollars
4           mnyDollars = CCur(Mid(strCurrency, 1, lngDecPos - 1))

            ' get amount after decimal point and multiply by 100 so cents is before decimal point
5           decRight = CDec(CDec(Mid(strCurrency, lngDecPos)) / 0.01)

            ' get cents by getting integer portion
6           decCents = Int(decRight)

            ' get leftover
7           decRight = CDec(decRight - decCents)

            ' if leftover is equal to or above round threshold
8           If decRight >= 0.5 Then

9              RoundPenny = mnyDollars + ((decCents + 1) * 0.01)

            ' if leftover is less than round threshold
10          Else

11             RoundPenny = mnyDollars + (decCents * 0.01)

12          End If

         ' if there is no decimal point
13       Else

            ' return it
14          RoundPenny = CCur(strCurrency)

15       End If

16       Exit Function

RoundPenny_Error:

17       Select Case Err.Number

            Case 6

18             Err.Raise vbObjectError + 334, c_strComponent & ".RoundPenny", "Number '" & strCurrency & "' is too big to represent as a currency value."

19          Case Else

20             DisplayError c_strComponent, "RoundPenny"

21       End Select

End Function
' ----------------------------------------------------------------------------- 

VBA.Round(1.23342, 2) // will return 1.23

чтобы решить проблему разбиения пенни, не добавляя сумму, из которой они были первоначально разбиты, я создал пользовательскую функцию.

Function PennySplitR(amount As Double, Optional splitRange As Variant, Optional index As Integer = 0, Optional n As Integer = 0, Optional flip As Boolean = False) As Double
' This Excel function takes either a range or an index to calculate how to "evenly" split up dollar amounts
' when each split amount must be in pennies.  The amounts might vary by a penny but the total of all the
' splits will add up to the input amount.

' Splits a dollar amount up either over a range or by index
' Example for passing a range: set range $I:$K to =PennySplitR($E,$I:$K) where $E is the amount and $I:$K is the range
'                              it is intended that the element calling this function will be in the range
' or to use an index and total items instead of a range: =PennySplitR($E,,index,N)
' The flip argument is to swap rows and columns in calculating the index for the element in the range.

' Thanks to: http://stackoverflow.com/questions/5559279/excel-cell-from-which-a-function-is-called for the application.caller.row hint.
Dim evenSplit As Double, spCols As Integer, spRows As Integer
If (index = 0 Or n = 0) Then
    spRows = splitRange.Rows.count
    spCols = splitRange.Columns.count
    n = spCols * spRows
    If (flip = False) Then
       index = (Application.Caller.Row - splitRange.Cells.Row) * spCols + Application.Caller.Column - splitRange.Cells.Column + 1
     Else
       index = (Application.Caller.Column - splitRange.Cells.Column) * spRows + Application.Caller.Row - splitRange.Cells.Row + 1
    End If
 End If
 If (n < 1) Then
    PennySplitR = 0
    Return
 Else
    evenSplit = amount / n
    If (index = 1) Then
            PennySplitR = Round(evenSplit, 2)
        Else
            PennySplitR = Round(evenSplit * index, 2) - Round(evenSplit * (index - 1), 2)
    End If
End If
End Function

я использовал следующие простой


вот простой способ всегда округлять до следующего целого числа в Access 2003:

BillWt = IIf([Weight]-Int([Weight])=0,[Weight],Int([Weight])+1)

например:

  • [вес] = 5.33; Int ([вес]) = 5; так 5.33-5 = 0.33 (0), таким образом, ответ BillWt = 5+1 = 6.
  • [вес] = 6.000, Int ([вес]) = 6 , поэтому 6.000-6 = 0, поэтому ответ BillWt = 6.