知识力量_圆靠二的力量

知识力量

不寻常的舍入 (Unusual rounding)

It may not be every day you are required to round a number by a power of two. But when the task shows up, here is how to get it done, also for non-trivial numbers like very large numbers or numbers with high precision (many decimals).

它可能不是每次都需要通过两个动力圆了许多天。 但是,当任务显示出来时,这也是完成任务的方法,也适用于非平凡的数字,例如非常大的数字或具有高精度的数字(许多小数)。


(
)
两个的力量 (The power of two)

In case you are not familiar with this, the power of two means 2n. For example, 24 equals 16. Other well-known values are listed here with n in the top row:

如果您对此不熟悉, 则两个的幂为 2 n 。 例如,2 4等于16。此处列出了其他众所周知的值,其中第一行为n

If n is positive, we get numbers like:

如果n正数 ,我们得到如下数字:

0
1
2
3
4
5
6
7
8
9
10
1
2
4
8
16
32
64
128
256
512
1024
0
1个
2
3
4
5
6
7
8
9
10
1个
2
4
8
16
32
64
128
256
512
1024

For example, 18 or 14 rounded by 8 results in 16.

例如,将18或14四舍五入为16。

If n is negative, we get numbers like:

如果n负数 ,我们得到如下数字:

0
1
2
3
4
5
6
7
8
9
10
1
1/2
1/4
1/8
1/16
1/32
1/64
1/128
1/256
1/512
1/1024
0
1个
2
3
4
5
6
7
8
9
10
1个
1/2
1/4
1/8
1/16
1/32
1/64
1/128
1/256
1/512
1/1024

For example, 1.7 or 1.8 rounded by 1/4 results in 1.75

例如,将1.7或1.8四舍五入为1/4将得出1.75

大数值和高精度 (Large values and high precision)

Rounding small numbers with few decimals is not a big deal. 

用小数点后一位舍入小数字不是什么大问题。

However, the goal is to be able to round both very large numbers and numbers with many decimals. To achieve this, data type  Decimal is used because of its much higher precision than Double.

但是,目标是能够将很大的数字和带有小数的数字都舍入。 为此,使用了数据类型Decimal ,因为它的精度比Double高得多。

The smallest fraction for rounding is: 

舍入的最小分数是:

2-21 which equals 1/2097152 or 0.000000476837158203125

2 -21等于1/2097152或0.000000476837158203125

The largest numerical value to round with maximum resolution is:

要以最大分辨率四舍五入的最大数值为:

79228162 + (221 - 8) / 221 which equals 79228162.999996185302734375

79228162 +(2月21日至 8 )/ 2 21,其等于79228162.999996185302734375

With lesser precision (fewer decimals) much larger values can be handled. 

如果精度较低(小数位数较少),则可以处理更大的值。

However, the expected rounded value must not exceed the range of:

但是,期望的舍入值不得超过以下范围:

± 79,228,162,514,264,337,593,543,950,335 which is close to ± 296

±79,228,162,514,264,337,593,543,950,335(接近±2 96)

功能 (The function)

Let's have a look at the main function that rounds by 4/5

让我们看一下四舍五入的主要功能

' Rounds Value by 4/5 to the power of two as specified with parameter Exponent.
'
' If Exponent is positive, the fraction of Value is rounded to an integer a fraction of 1 / 2 ^ Exponent.
' If Exponent is zero, Value is rounded to an integer.
' If Exponent is negative, Value is rounded to an integer and a multiplum of 2 ^ Exponent.
'
' Rounds correctly Value until max/min value limited by a scaling of 2 raised to the power of Exponent.
'
' Smallest fraction for rounding is:
'   2 ^ -21 (= 1 / 2097152)
' or:
'   0.000000476837158203125
'
' Largest numerical value to round with maximum resolution is:
'   79228162 + (2 ^ 21 - 8) / 2 ^ 21
' or:
'   79228162.999996185302734375
'
' Expected rounded value must not exceed the range of:
'   +/- 79,228,162,514,264,337,593,543,950,335
'
' Uses CDec() to prevent bit errors of reals.
'
' Execution time is about 1µs.
'
' Examples, integers:
'   RoundMidBase2(1001, -3)             -> 1000
'   RoundMidBase2(1001, -8)             -> 1024
'   RoundMidBase2(17.03, -4)            ->   16
'   RoundMidBase2(17.03, -5)            ->   32
'
' Examples, decimals:
'   1 / 2 ^ 4 = 0.0625                              Step value when rounding by 1/16
'   RoundMidBase2(17.03, 4)             -> 17.0     Rounding down
'   RoundMidBase2(17.08, 4)             -> 17.0625  Rounding down
'   RoundMidBase2(17.1, 4)              -> 17.125   Rounding up
'   RoundMidBase2(17.2, 4)              -> 17.1875  Rounding down
'
'   1 / 2 ^ 5 = 0.03125                             Step value when rounding by 1/32
'   RoundMidBase2(17.125 + 0.00000, 4)  -> 17.125   Exact value. No rounding.
'   RoundMidBase2(17.125 + 0.03124, 4)  -> 17.125   Rounding down
'   RoundMidBase2(17.125 + 0.03125, 4)  -> 17.1875  Rounding up
'
' More info on the power of two and rounding:
'   https://en.wikipedia.org/wiki/Power_of_two
'
' 2018-04-01. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function RoundMidBase2( _
    ByVal Value As Variant, _
    Optional ByVal Exponent As Long) _
    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(Base2 ^ Exponent)
       
        If Scaling = 0 Then
            ' A very large value for Exponent has minimized scaling.
            ' Return Value as is.
            ReturnValue = Value
        Else
            ' Standard 4/5 rounding.
            ' Very large values for Exponent 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
   
    RoundMidBase2 = ReturnValue

End Function 

The core is the scaling:

核心是扩展:

Scaling = CDec(Base2 ^ Exponent) 

where Base2 ^ Exponent is the power of two to use.

其中Base2 ^ Expnent是使用2的幂。

The usage is quite simple, for example for integer rounding:

用法非常简单,例如整数舍入:

RoundedValue = RoundMidBase2(17.03, -4)
' RoundedValue -> 16 

and for rounding of fractions:

对于四舍五入:

RoundedValue = RoundMidBase2(17.1, 4)
' RoundedValue -> 17.125 

Note, that for parameter Exponent, a positive value will round to fractions, while a negative will round to integer. This is to keep the syntax identical to that of function RoundMid, where Exponent indicates the count of decimals.

请注意,对于参数Exponent,正值将舍入为分数,而负数将舍入为整数。 这是为了使语法与函数RoundMid相同,其中Exponent表示小数计数。

替代舍入-向上或向下 (Alternative rounding - up or down)

In some cases, rounding by 4/5 is not suitable. Depending on the scenario, it might be more feasible to round either up or down, thus two supplemental functions are included. They are built much similar to RoundMidBase2 and the sister functions, RoundUp and RoundDown.

在某些情况下,不适合四舍五入。 根据情况, 向上向下取整可能更可行,因此包括两个补充功能。 它们的构建与RoundMidBase2及其姐妹函数RoundUpRoundDown非常相似。

To round up, use the function RoundUpBase2

要向上舍入,请使用功能RoundUpBase2

' Rounds Value up to the power of two as specified with parameter Exponent.
'
' If Exponent is positive, the fraction of Value is rounded to an integer a fraction of 1 / 2 ^ Exponent.
' If Exponent is zero, Value is rounded to an integer.
' If Exponent is negative, Value is rounded to an integer and a multiplum of 2 ^ Exponent.
'
' Optionally, rounds negative values away from zero.
'
' Rounds correctly Value until max/min value limited by a scaling of 2 raised to the power of Exponent.
'
' Smallest fraction for rounding is:
'   2 ^ -21 (= 1 / 2097152)
' or:
'   0.000000476837158203125
'
' Largest numerical value to round with maximum resolution is:
'   79228162 + (2 ^ 21 - 8) / 2 ^ 21
' or:
'   79228162.999996185302734375
'
' Expected rounded value must not exceed the range of:
'   +/- 79,228,162,514,264,337,593,543,950,335
'
' Uses CDec() to prevent bit errors of reals.
'
' Execution time is about 0.5µs for rounding to integer, else about 1µs.
'
' Examples, integers:
'   RoundUpBase2(1001, -3)              -> 1008
'   RoundUpBase2(1001, -8)              -> 1024
'   RoundUpBase2(17.03, -4)             ->   32
'   RoundUpBase2(17.03, -5)             ->   32
'
' Examples, decimals:
'   1 / 2 ^ 4 = 0.0625                              Step value when rounding by 1/16
'   RoundUpBase2(17.03, 4)              -> 17.0625
'   RoundUpBase2(17.08, 4)              -> 17.125
'   RoundUpBase2(17.1, 4)               -> 17.125
'   RoundUpBase2(17.2, 4)               -> 17.25
'
'   1 / 2 ^ 5 = 0.03125                             Step value when rounding by 1/32
'   RoundUpBase2(17.125 + 0.00000, 4)   -> 17.125   Exact value. No rounding.
'   RoundUpBase2(17.125 + 0.03124, 4)   -> 17.1875
'   RoundUpBase2(17.125 + 0.03125, 4)   -> 17.1875
'
' More info on the power of two and rounding:
'   https://en.wikipedia.org/wiki/Power_of_two
'
' 2018-04-02. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function RoundUpBase2( _
    ByVal Value As Variant, _
    Optional ByVal Exponent As Long, _
    Optional ByVal RoundingAwayFromZero As Boolean) _
    As Variant
    Dim Scaling     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
        If Exponent <> 0 Then
            Scaling = CDec(Base2 ^ Exponent)
        Else
            Scaling = 1
        End If
        If Scaling = 0 Then
            ' A very large value for Exponent has minimized scaling.
            ' Return Value as is.
            ReturnValue = Value
        ElseIf RoundingAwayFromZero = False Or Value > 0 Then
            ' Round numeric value up.
            If Scaling = 1 Then
                ' Integer rounding.
                ReturnValue = -Int(-Value)
            Else
                ' First try with conversion to Decimal to avoid bit errors for some reals like 32.675.
                On Error Resume Next
                ScaledValue = -Int(CDec(-Value) * Scaling)
                ReturnValue = ScaledValue / Scaling
                If Err.Number <> 0 Then
                    ' Decimal overflow.
                    ' Round Value without conversion to Decimal.
                    ScaledValue = -Int(-Value * Scaling)
                    ReturnValue = ScaledValue / Scaling
                End If
            End If
        Else
            ' Round absolute value up.
            If Scaling = 1 Then
                ' Integer rounding.
                ReturnValue = Int(Value)
            Else
                ' First try with conversion to Decimal to avoid bit errors for some reals like 32.675.
                On Error Resume Next
                ScaledValue = Int(CDec(Value) * Scaling)
                ReturnValue = ScaledValue / Scaling
                If Err.Number <> 0 Then
                    ' Decimal overflow.
                    ' Round Value without conversion to Decimal.
                    ScaledValue = Int(Value * Scaling)
                    ReturnValue = ScaledValue / Scaling
                End If
            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
   
    RoundUpBase2 = ReturnValue
End Function 

and to round down, use the function RoundDownBase2

要向下取整,请使用功能RoundDownBase2

' Rounds Value down to the power of two as specified with parameter Exponent.
'
' If Exponent is positive, the fraction of Value is rounded to an integer a fraction of 1 / 2 ^ Exponent.
' If Exponent is zero, Value is rounded to an integer.
' If Exponent is negative, Value is rounded to an integer and a multiplum of 2 ^ Exponent.
'
' Optionally, rounds negative values towards zero.
'
' Rounds correctly Value until max/min value limited by a scaling of 2 raised to the power of Exponent.
'
' Smallest fraction for rounding is:
'   2 ^ -21 (= 1 / 2097152)
' or:
'   0.000000476837158203125
'
' Largest numerical value to round with maximum resolution is:
'   79228162 + (2 ^ 21 - 8) / 2 ^ 21
' or:
'   79228162.999996185302734375
'
' Expected rounded value must not exceed the range of:
'   +/- 79,228,162,514,264,337,593,543,950,335
'
' Uses CDec() to prevent bit errors of reals.
'
' Execution time is about 0.5µs for rounding to integer, else about 1µs.
'
' Examples, integers:
'   RoundDownBase2(1001, -3)                -> 1000
'   RoundDownBase2(1001, -8)                ->  768
'   RoundDownBase2(17.03, -4)               ->   16
'   RoundDownBase2(17.03, -5)               ->    0
'
' Examples, decimals:
'   1 / 2 ^ 4 = 0.0625                                  Step value when rounding by 1/16
'   RoundDownBase2(17.03, 4)                -> 17
'   RoundDownBase2(17.08, 4)                -> 17.0625
'   RoundDownBase2(17.1, 4)                 -> 17.0625
'   RoundDownBase2(17.2, 4)                 -> 17.1875
'
'   1 / 2 ^ 5 = 0.03125                                 Step value when rounding by 1/32
'   RoundDownBase2(17.125 + 0.00000, 4)     -> 17.125   Exact value. No rounding.
'   RoundDownBase2(17.125 + 0.03124, 4)     -> 17.125
'   RoundDownBase2(17.125 + 0.03125, 4)     -> 17.125
'
' More info on the power of two and rounding:
'   https://en.wikipedia.org/wiki/Power_of_two
'
' 2018-04-02. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function RoundDownBase2( _
    ByVal Value As Variant, _
    Optional ByVal Exponent As Long, _
    Optional ByVal RoundingToZero As Boolean) _
    As Variant
   
    Dim Scaling     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
        If Exponent <> 0 Then
            Scaling = CDec(Base2 ^ Exponent)
        Else
            Scaling = 1
        End If
        If Scaling = 0 Then
            ' A very large value for Exponent has minimized scaling.
            ' Return Value as is.
            ReturnValue = Value
        ElseIf RoundingToZero = False Then
            ' Round numeric value down.
            If Scaling = 1 Then
                ' Integer rounding.
                ReturnValue = Int(Value)
            Else
                ' First try with conversion to Decimal to avoid bit errors for some reals like 32.675.
                ' Very large values for Exponent can cause an out-of-range error when dividing.
                On Error Resume Next
                ScaledValue = Int(CDec(Value) * Scaling)
                ReturnValue = ScaledValue / Scaling
                If Err.Number <> 0 Then
                    ' Decimal overflow.
                    ' Round Value without conversion to Decimal.
                    ScaledValue = Int(Value * Scaling)
                    ReturnValue = ScaledValue / Scaling
                End If
            End If
        Else
            ' Round absolute value down.
            If Scaling = 1 Then
                ' Integer rounding.
                ReturnValue = Fix(Value)
            Else
                ' First try with conversion to Decimal to avoid bit errors for some reals like 32.675.
                ' Very large values for NumDigitsAfterDecimal can cause an out-of-range error when dividing.
                On Error Resume Next
                ScaledValue = Fix(CDec(Value) * Scaling)
                ReturnValue = ScaledValue / Scaling
                If Err.Number <> 0 Then
                    ' Decimal overflow.
                    ' Round Value with no conversion.
                    ScaledValue = Fix(Value * Scaling)
                    ReturnValue = ScaledValue / Scaling
                End If
            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
   
    RoundDownBase2 = ReturnValue
End Function 

For both functions, you will again see, that Decimal is used as widely as possible to preserve a high precision, and only for very large values - in case of an overflow - falls back to use Double.

对于这两个函数,您将再次看到,十进制被广泛使用以保持高精度,并且仅对于非常大的值(在发生溢出的情况下)退回到使用Double。

公开结果 (Expose the result)

Since the functions will return a rounded fraction as the nearest equivalent decimal value, it will likely be difficult to check or verify the result visually. 

由于函数将返回四舍五入的分数作为最接近的等效十进制值,因此可能很难直观地检查或验证结果。

For example, who can tell if this result value for RoundedValue is as expected:

例如,谁可以说出RoundedValue的结果值是否符合预期:

RoundedValue = RoundMidBase2(17.22, 17)
' RoundedValue -> 17.220001220703125 

To reveal, that this incomprehensive decimal number expresses:

为了揭示这一不完整的十进制数字表示:

17 7209/32768

17 7209/32768

another function, ConvertDecimalFractions, can be used. 

可以使用另一个函数ConvertDecimalFractions

First, an Enum is created to be used when specifying the rounding method:

首先,创建一个枚举,以供指定舍入方法时使用:

Public Enum rmRoundingMethod
    Down = -1
    Midpoint = 0
    Up = 1
End Enum 

You'll recognize this as the data type of the fifth parameter, RoundingMethod, where it defaults to use 4/5 rounding:

您会将其识别为第五个参数RoundingMethod的数据类型,默认情况下使用4/5舍入:

' Rounds and converts a decimal value to an integer and the fraction of an integer
' using 4/5 midpoint rounding, optionally rounding up or down.
'
' Rounding method is determined by parameter RoundingMethod.
' For rounding up or down, rounding of negative values can optionally be set to
' away-from-zero or towards-zero respectively by parameter RoundingAsAbsolute.
'
' Returns the rounded value as a decimal.
' Returns numerator and denominator of the fraction by reference.
'
' For general examples, see function RoundMidBase2, RoundUpBase2, and RoundDownBase2.
'
' Will, for example, convert decimal inches to integer inches and a fraction of inches.
' However, numerator and denominator of the fraction are returned by reference in the
' parameters Numerator and Denominator for the value to be formatted as text by the
' calling procedure.
'
' Example:
'   Value = 7.22
'   Exponent = 2    ' will round to 1/4.
'   Numerator = 0
'   Denominator = 0
'
'   Result = ConvertDecimalFractions(Value, Exponent, Numerator, Denominator)
'
'   Result = 7.25
'   Numerator = 1
'   Denominator = 4
'
'   Result = ConvertDecimalFractions(Value, Exponent, Numerator, Denominator, Up)
'
'   Result = 7.25
'   Numerator = 1
'   Denominator = 4
'
'   Result = ConvertDecimalFractions(Value, Exponent, Numerator, Denominator, Down)
'
'   Result = 7
'   Numerator = 0
'   Denominator = 0
'
' If negative, parameter Exponent determines the rounding of the fraction as
' 1 / 2 ^ Exponent with a maximum of 21 - or from 1 / 2 to 1 / 2097152.
' For inches, that is a range from 12.7 mm to about 12.1 nm.
'
' If zero or positive, parameter Exponent determines the rounding of the
' integer value with 2 ^ Exponent with a maximum of 21 - or from 1 to 2097152.
' For inches, that is a range from 25.4 mm to about 53.27 km.
'
' Also, se comments for the required functions:
'
'   RoundUpBase2
'   RoundMidBase2
'   RoundDownBase2
'
' 2018-04-05. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function ConvertDecimalFractions( _
    ByVal Value As Variant, _
    ByVal Exponent As Integer, _
    Optional ByRef Numerator As Long, _
    Optional ByRef Denominator As Long, _
    Optional RoundingMethod As rmRoundingMethod = Midpoint, _
    Optional RoundingAsAbsolute As Boolean) _
    As Variant
    
    Dim Number      As Variant
    Dim Fraction    As Variant
    
    ' Validate rounding method.
    Select Case RoundingMethod
        Case Up, Midpoint, Down
            ' OK.
        Case Else
            ' Use default rounding method.
            RoundingMethod = Midpoint
    End Select
    
    If Exponent <= 0 Then
        ' Integer rounding only.
        Select Case RoundingMethod
            Case Up
                Number = RoundUpBase2(Value, Exponent, RoundingAsAbsolute)
            Case Midpoint
                Number = RoundMidBase2(Value, Exponent)
            Case Down
                Number = RoundDownBase2(Value, Exponent, RoundingAsAbsolute)
        End Select
        Fraction = 0
        Numerator = 0
        Denominator = 0
    Else
        ' Rounding with fractions.
        Number = Fix(CDec(Value))
        Select Case RoundingMethod
            Case Up
                Fraction = RoundUpBase2(Value - Number, Exponent, RoundingAsAbsolute)
            Case Midpoint
                Fraction = RoundMidBase2(Value - Number, Exponent)
            Case Down
                Fraction = RoundDownBase2(Value - Number, Exponent, RoundingAsAbsolute)
        End Select
        
        If Fraction = 0 Or Abs(Fraction) = 1 Then
            ' Fraction has been rounded to 0 or +/-1.
            Numerator = 0
            Denominator = 0
        Else
            ' Calculate numerator and denominator for the fraction.
            Denominator = Base2 ^ Exponent
            Numerator = Fraction * Denominator
            ' Find the smallest denominator.
            While Numerator Mod Base2 = 0
                Numerator = Numerator / Base2
                Denominator = Denominator / Base2
            Wend
        End If
    End If
    
    ConvertDecimalFractions = Number + Fraction
    
End Function 

Using the previous functions, this function rounds and converts a decimal value to an integer and the fraction of an integer, and then returns:

使用以前的函数,此函数将十进制值四舍五入并将其转换为整数和整数的分数,然后返回:

  • the rounded value as a decimal number as the output

    四舍五入后的值作为十进制数作为输出
  • the numerator and denominator of the fraction as integers by reference

    小数的分子和分母 通过引用为整数

An example will illustrate this. Here is a test value and a set of parameters and the resulting values:

一个例子将说明这一点。 这是一个测试值以及一组参数和结果值:

Value = 7.22
Exponent = 2    ' will round to 1/4.
Numerator = 0
Denominator = 0

Result = ConvertDecimalFractions(Value, Exponent, Numerator, Denominator)

IntegerResult = Fix(Result)

' Result        -> 7.25
' IntegerResult -> 7
' Numerator     -> 1
' Denominator   -> 4 

By applying some formatting to the output values and concatenate these as a string, the result could be presented as:

通过对输出值应用某种格式并将它们连接为字符串,结果可以表示为:

7 1/4

7 1/4

最小分母 (Smallest denominator)

It is not difficult to calculate "a" numerator and denominator. However, you wouldn't want 1/4 in the example above to be returned as 4/16 or 256/1024.

计算“ a”分子和分母并不难。 但是,您不希望在上面的示例中将1/4返回为4/16或256/1024。

This is taken care of in the loop:

这是在循环中处理的:

            ' Calculate numerator and denominator for the fraction.
            Denominator = Base2 ^ Exponent
            Numerator = Fraction * Denominator
            ' Find the smallest denominator.
            While Numerator Mod Base2 = 0
                Numerator = Numerator / Base2
                Denominator = Denominator / Base2
            Wend 

The trick is to calculate the modulus 2 of the numerator. If zero, the numerator is even and can be divided by 2, which is done before the next loop. When reaching an uneven numerator, here 1, the loop exits, and we have the smallest numerator and denominator.

诀窍是计算分子的模数2 。 如果为零,则分子为偶数并可除以2,这是在下一个循环之前完成的。 当到达一个不均匀的分子(这里为1)时,循环退出,而我们的分子和分母最小。

调试 (Debugging)

Most people, even experienced programmers, have a hard time determining the Base 2 components of numbers. For small integers, they can be calculated relatively fast, but for larger values and for decimals beyond the most trivial - as 1/4 and 1/8 - it quickly gets very hard.

大多数人,甚至是经验丰富的程序员,都很难确定数字的以2为底的成分。 对于较小的整数,它们可以相对快速地进行计算,但是对于较大的值和超出最琐碎的小数部分(如1/4和1/8),很快就会变得非常困难。

So, to help debugging a number, a function has been created, which extracts and lists the Base 2 components of the number, DebugBase2

因此,为了帮助调试数字,已创建一个函数,该函数提取并列出数字的Base 2组件DebugBase2

' Rounds a value and prints the result in its Base 2 components
' and as a decimal value, and in a integer-fraction format.
'
' Will accept values within +/- 2 ^ 96.
' Also, se comments for the required functions:
'
'   ConvertDecimalFractions
'   RoundMidBase2
'   Log2
'
' Example:
'
'   DebugBase2 746.873, 2
'   Exponent      2 ^ Exponent  Factor        Value         Fraction
'    9             512           1             512
'    8             256           0             0
'    7             128           1             128
'    6             64            1             64
'    5             32            1             32
'    4             16            0             0
'    3             8             1             8
'    2             4             0             0
'    1             2             1             2
'    0             1             0             0
'   Total:                                     746          3/4
'   Decimal:                                   746.75
'
' 2018-03-09. Gustav Brock, Cactus Data ApS, CPH.
'
Public Sub DebugBase2( _
    ByVal Value As Variant, _
    Optional ByVal Exponent As Long)
   
    ' Maximum possible exponent.
    Const MaxExponent2  As Long = 96

    Dim Exponent2       As Long
    Dim Number          As Variant
    Dim Rounded         As Variant
    Dim Factor          As Long
    Dim Sign            As Long
    Dim Numerator       As Long
    Dim Denominator     As Long

    If Not IsNumeric(Value) Then Exit Sub

    Sign = Sgn(Value)
    If Sign = 0 Then Exit Sub
   
    Number = CDec(Fix(Abs(Value)))
   
    ' Split and print the integer part.
    Debug.Print "Exponent", "2 ^ Exponent", "Factor", "Value", "Fraction"
    If Number > 0 Then
        ' Print each bit and value.
        For Exponent2 = Int(Log2(Number)) To 0 Step -1
            If Exponent2 = MaxExponent2 Then
                ' Cannot perform further calculation.
                Factor = 1
                Number = 0
            Else
                Factor = Int(Number / CDec(Base2 ^ Exponent2))
                Number = Number - CDec(Factor * Base2 ^ Exponent2)
            End If
            Debug.Print Exponent2, Base2 ^ Exponent2, Factor, Sign * Factor * Base2 ^ Exponent2
        Next
    Else
        ' Print zero values.
        Debug.Print 0, 0, 0, 0
    End If
   
    ' Find and print the fraction.
    Rounded = ConvertDecimalFractions(Value, Exponent, Numerator, Denominator)
    Debug.Print "Total:", , , CDec(Fix(Value)), Numerator & "/" & Denominator
    Debug.Print "Decimal:", , , Rounded
   
End Sub 

Study the in-line comments to follow the steps it takes to break down the value.

研究内联注释,以遵循分解值所需要执行的步骤。

It uses a helper function, Log2, to limit the search for components from the maximum of 96 to those that are present:

它使用一个辅助函数Log2来限制对最多96个组件的搜索:

' Returns Log 2 of Value.
'
' 2018-02-20. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function Log2( _
    ByVal Value As Double) _
    As Double

    ' No error handling as this should be handled
    ' outside this function.
    '
    ' Example:
    '
    '     If MyValue > 0 then
    '         LogMyValue = Log2(MyValue)
    '     Else
    '         ' Do something else ...
    '     End If
   
    Log2 = Log(Value) / Log(Base2)

End Function 

If you, for example, call it with these parameters:

例如,如果使用以下参数调用它:

DebugBase2 746.873, 2 

it will round the value and list the components of the rounded value - and also the rounded value as a decimal number as well as an integer and fraction:

它将舍入该值并列出舍入后的值的组成部分-还将舍入后的值显示为十进制数字以及整数和小数:

Exponent      2 ^ Exponent  Factor        Value         Fraction
9             512           1             512
8             256           0             0
7             128           1             128
6             64            1             64
5             32            1             32
4             16            0             0
3             8             1             8
2             4             0             0
1             2             1             2
0             1             0             0
Total:                                    746          3/4
Decimal:                                  746.75 

结论 (Conclusion)

Armed with these functions, you can - similar to normal rounding to decimals or integers - easily round values by the power of two with the high precision possible in VBA using Decimal. Also, the result can be a decimal or a set of values for the integer part and the fraction.

有了这些功能,您可以-与普通舍入到小数或整数一样-可以使用Decimal在VBA中以高精度将值以2的幂进行舍入。 同样,结果可以是小数或整数部分和分数的一组值。

Last but not least, the result can be presented or debugged in ways that are easy for the human eye to comprehend.

最后但并非最不重要的一点是,可以以易于人眼理解的方式来呈现或调试结果。

进一步阅读 (Further reading)

More info on the power of two and rounding can be found at Wikipedia - Power of two

有关2的幂和取整的更多信息,请参见Wikipedia-2的幂

The previous article on decimal rounding: Rounding values up, down, by 4/5, or to significant figures

上一篇有关十进制舍入的文章:将值向上,向下,四舍五入或有效数字四舍五入

资料下载 (Downloads)

The current version can always be found at GitHub.

当前版本始终可以在GitHub上找到

The initial version is here: Rounding 1.3.1.zip This includes a Microsoft Access 2016 project.

初始版本在这里: 四舍五入1.3.1.zip这包括Microsoft Access 2016项目。

I hope you found this article useful. You are encouraged to ask questions, report any bugs or make any other comments about it below.

希望本文对您有所帮助。 鼓励您在下面提出问题,报告任何错误或对此作出任何其他评论。

Note: If you need further "Support" about this topic, please consider using the Ask a Question feature of Experts Exchange. I monitor questions asked and would be pleased to provide any additional support required in questions asked in this manner, along with other EE experts.

注意 :如果您需要有关此主题的更多“支持”,请考虑使用Experts Exchange 的“提问”功能。 我会监督提出的问题,并很高兴与其他电子工程师一起为以这种方式提出的问题提供所需的任何其他支持。

Please do not forget to press the "Thumbs Up" button if you think this article was helpful and valuable for EE members.

如果您认为本文对EE成员有用且有价值,请不要忘记按下“竖起大拇指”按钮。

翻译自: https://www.experts-exchange.com/articles/31859/Round-by-the-power-of-two.html

知识力量

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值