Search code examples
excelintworksheet-functionlogarithmvba

Inconsistency between Excel Worksheet and VBA INT(LOG()) Functions


In a standard Excel VBA code module I’ve created custom formula MyLog10() to get 10-base Logarithm:

Public Function MyLog10(ByVal dbl_Input As Double) As Double
    MyLog10 = Log(dbl_Input) / Log(10)
End Function

Tested for the value of 1000 and got the accurate result of 3. Surprisingly, when applied the Int() VBA Function to the result of MyLog10(), it came out as 2. The same was observed with Fix() VBA function (see the following test Sub):

Sub TestIntMyLog10()
    Debug.Print MyLog10(1000) 'Result 3 (accurate)
    Debug.Print Int(MyLog10(1000)) 'Result 2  (inaccurate)
    Debug.Print Fix(MyLog10(1000)) 'Result 2  (inaccurate)
End Sub

Even more surprising, my custom Function MyLog10(1000) entered in Excel Worksheet as =MyLog10(1000) and =INT(MyLog10(1000)) in both cases returns the correct result of 3.

Any explanation for this VBA/Worksheet inconsistency (probably bug)?


Also, by adding a very small number (1E-12) to the output of custom VBA MyLog10() produced the correct result in both cases (with/without additional Int()):

Debug.Print Int (Log10(1000)+1E-12) 'Result 3 (accurate)

Solution

  • After trying multiple type conversions, the solution was found: it is using a Decimal type as custom Log Function MyLog10Decimal return type. Following is the code snippet containing a universal VBA solution and test Sub TestIntMyLog10():

    ' SOLUTION: using output type decimal : Int() and Fix() return correct value
    Public Function MyLog10Decimal(ByVal dbl_Input As Double) As Variant
        MyLog10Decimal = CDec(Log(dbl_Input) / Log(10))
    End Function
    
    'Problem: using output type double: Int() and Fix() may return incorrect
    Public Function MyLog10(ByVal dbl_Input As Double) As Double
         MyLog10 = Log(dbl_Input) / Log(10)
    End Function
    
    Sub TestIntMyLog10()
       ' ** test sample: input number 1000
       'using Log output type double: Int() and Fix() produces incorrect results
        Debug.Print MyLog10(1000) 'Result 3 (accurate)
        Debug.Print Int(MyLog10(1000)) 'Result 2  (inaccurate)
        Debug.Print Fix(MyLog10(1000)) 'Result 2  (inaccurate)
    
        'using Log output type decimal: Int() and Fix() produces correct results
        Debug.Print Int(MyLog10Decimal(1000)) 'Result 3  (accurate)
        Debug.Print Int(MyLog10Decimal(1000)) 'Result 3  (accurate)
        Debug.Print Fix(MyLog10Decimal(1000)) 'Result 3  (accurate)
    
       ' ** test sample: input number 0.001
       'using Log output type double: Fix() produces incorrect results
        Debug.Print MyLog10(0.001) 'Result -3 (accurate)
        Debug.Print Int(MyLog10(0.001)) 'Result -3  (accurate)
        Debug.Print Fix(MyLog10(0.001)) 'Result -2  (inaccurate)
    
        'using Log output type decimal: Int() and Fix() produces correct results
        Debug.Print Int(MyLog10Decimal(0.001)) 'Result -3  (accurate)
        Debug.Print Int(MyLog10Decimal(0.001)) 'Result -3  (accurate)
        Debug.Print Fix(MyLog10Decimal(0.001)) 'Result -3  (accurate)
    End Sub
    

    A bit of explanation. VBA does not support Decimal type as Function parameter/output, so it's declared a Variant with CDec conversion applied to it. So far, it was tested on multiple inputs (>1 and <1) and returns correct results. Also, Excel Worksheet contains a built-in function LOG10(), but VBA does not. Thus, this VBA solution may be used in other Office app without need for Excel Object Library reference.