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).
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:
If n is positive, we get numbers like:
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.
If n is negative, we get numbers like:
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
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.
The smallest fraction for rounding is:
2-21 which equals 1/2097152 or 0.000000476837158203125
The largest numerical value to round with maximum resolution is:
79228162 + (221 - 8) / 221 which equals 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
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.
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.
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.
To round up, use the function 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
' 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.
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 = RoundMidBase2(17.22, 17)
' RoundedValue -> 17.220001220703125
To reveal, that this incomprehensive decimal number expresses:
17 7209/32768
another function, ConvertDecimalFractions, can be used.
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:
' 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:
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
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.
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.
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.
So, to help debugging a number, a function has been created, which extracts and lists the Base 2 components of the number, 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:
' 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
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.
Last but not least, the result can be presented or debugged in ways that are easy for the human eye to comprehend.
More info on the power of two and rounding can be found at Wikipedia - Power of two
The previous article on decimal rounding:
Rounding values up, down, by 4/5, or to significant figures
The current version can always be found at GitHub.
The initial version is here: Rounding 1.3.1.zip This includes a Microsoft Access 2016 project.
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.
Please do not forget to press the "Thumbs Up" button if you think this article was helpful and valuable for EE members.
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (0)