Как округлить в MS Access, VBA - PullRequest
12 голосов
/ 26 сентября 2008

Какой способ округлить в VBA Access?

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

Excel.WorksheetFunction.Round(...

Но я ищу средство, которое не зависит от Excel.

Ответы [ 12 ]

25 голосов
/ 26 сентября 2008

Будьте внимательны, функция округления VBA использует округление Banker's, где оно округляет .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 для моего округления, но обнаружил, что в Fix исправлена ​​ошибка, когда вы пытаетесь присвоить ей число, например 58,55; функция, дающая результат 58,5 вместо 58,6. Затем я наконец обнаружил, что вы можете использовать функцию Excel Worksheet Round, например:

Application.Round (58,55, 1)

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

9 голосов
/ 06 ноября 2008

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

"Функция 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

4 голосов
/ 03 октября 2008

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

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

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

, который, кажется, работает нормально

2 голосов
/ 01 мая 2016

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

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

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

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

или здесь:

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

Код только на GitHub:

VBA.Round

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

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

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

  • Округление на 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
2 голосов
/ 01 октября 2008
1 place = INT(number x 10 + .5)/10
3 places = INT(number x 1000 + .5)/1000

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

например If A > B Then MaxAB = A Else MaxAB = B примерно в 40 раз быстрее, чем использование ExcelWorksheetFunction.Max

2 голосов
/ 26 сентября 2008

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

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

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

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

1 голос
/ 25 февраля 2009

Лэнс уже упоминал о округлении наследования 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
' ----------------------------------------------------------------------------- 
1 голос
/ 26 сентября 2008

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

return int(var + 0.5)

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

0 голосов
/ 07 мая 2014

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

Function RoundUp(Number As Variant)
   RoundUp = Int(-100 * Number) / -100
   If Round(Number, 2) = Number Then RoundUp = Number
End Function

, но это ВСЕГДА округлит до 2 десятичных знаков и может также привести к ошибке.

, даже если оно отрицательное, оно округляется вверх (-1.011 будет -1.01, а 1.011 будет 1.02)

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

Function RoundUp(Number As Variant, Optional RoundDownIfNegative As Boolean = False)
On Error GoTo err
If Number = 0 Then
err:
    RoundUp = 0
ElseIf RoundDownIfNegative And Number < 0 Then
    RoundUp = -1 * Int(-100 * (-1 * Number)) / -100
Else
    RoundUp = Int(-100 * Number) / -100
End If
If Round(Number, 2) = Number Then RoundUp = Number
End Function

(используется в модуле, если это не очевидно)

0 голосов
/ 27 марта 2014

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

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$18:$K$21 to =PennySplitR($E$15,$I$18:$K$21) where $E$15 is the amount and $I$18:$K$21 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$15,,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
...