Excel VBA strange result returning integer of the result of a mathematical sum

Thebatfink

Active Member
Joined
Apr 8, 2007
Messages
410
Hi, I am scratching my head and can not see what the issue is here..

If I enter a formula in a cell
VBA Code:
=INT(559/1.118)
I get the result, 500 as I expect.

If I attempt this in VBA, I consistently get the result 499?? Examples:
VBA Code:
Private Sub test()
Dim a As Long
Dim b As Double
Dim c As Double
a = 559
b = 1.118
c = a / b
MsgBox c
MsgBox Int(c)
MsgBox Fix(c)
End Sub
c is returned as 500 as you would expect. But with either Int or Fix, it returns 499 in the message prompt.

VBA Code:
Private Sub test2()
MsgBox Int(559 / 1.118)
End Sub
499 is returned in the message prompt.

Why does it do this? I suspect it is something to do with floating point precision perhaps, but I don't understand why exactly?
 

Excel Facts

What does custom number format of ;;; mean?
Three semi-colons will hide the value in the cell. Although most people use white font instead.
I guess I should explain what it is I am actually trying to do. I am looping through numbers 1 through 99999. For each number I am dividing it by 1.118. I then want to test if the result is a whole number and if it is not, I want to extract the decimal portion. So for example:

1 / 1.118 = 0.8944543828264758 so this is not a whole number and the decimal portion is 0.8944543828264758
..
558 / 1.118 = 499.1055456171735 so this is not a whole number and the decimal portion is 0.1055456171735
559 / 1.118 = 500 so this is a whole number and the decimal portion is 0
..
99999 / 1.118..
 
Upvote 0
What version of Excel do you use?

Are you planning on a sum of the remainders or are you looking to list them?
 
Upvote 0
I'm using 2013. For the remainders I store it in one variable and for each loop from 1 to 99999, I evaluate if the current remainder is smaller than the one stored, if it is smaller, I replace the value in the variable with the smaller and will evaluate again in the next loop. So at the end I have the smallest possible remainder.

I think I might have stumbled over the problem, it seems to work but it would be nice to have confirmation as I originally thought my first solution was correct then it turned out it wasn't!

1 / 1.118 for example = 0.894454382826476, which is 15 decimal places but double datatype is rounding to 14 decimal places max, very slightly smaller than it really is. When I subsequently expect 500, I actually have 499.9999999999 or something so the Fix/Int functions returns 499.

I changed my variables to variant type and when I make and store these calculations to those variables I am wrapping them inside convert decimal function to retain the extra precision I believe as they then are treated as decimal datatype instead of double. At least now my issue with 559/1.118 coming out as integer value 499 seems resolved and it agrees it is now 500..

VBA Code:
Private Sub test()
Dim a As Long
Dim b As Double
Dim c As Variant
a = 559
b = 1.118
c = Int(CDec(a / b))
MsgBox c
End Sub

So assuming the above problem / solution is correct.. I think wherever I have a calculation and it might exceed 14 decimal places I need to do this. I guess I didn't think that kind of precision was needed in this scenario, makes me wonder where else I have overlooked this.
 
Upvote 0
I think I might have stumbled over the problem, it seems to work but it would be nice to have confirmation

You made a number of misstatements, and your use of CDec is unnecessary.

If you want to emulate Excel INT using VBA Int, the following demonstrates a straight-forward and reliable way to do it.

VBA Code:
Sub doit()
Dim a As Long, b As Double, c As Double
a = 559
b = 1.118
c = a / b
MsgBox Int(c) & vbNewLine & Int(c & "")
End Sub

The first Int displays 499. The second Int displays 500.

-----

The point is: Excel INT rounds its argument to 15 significant decimal digits before truncating to an integer.

Converting a numeric value to a string (e.g. c & "") is one way to round to 15 significant digits.

Although that provides the desired result in this particular case, it is actually a defect, IMHO.

For example, a typical method of randomly selecting from an array n items indexed from zero is to calculate the index =INT(n*RAND()).

We expect the result to be 0 to n-1 because RAND() is less than 1.

But if n=20 and RAND() returns 0.999999999999997+4.44E-16 or greater, for example, INT(n*RAND()) returns 20, not 19.

(I actually entered that error with RAND in Excel 2003. It is unclear whether RAND in Excel 2010 and later returns a value that large. But of course, it is the principle, not the example, that matters.)

-----

VBA Int does not round its argument to decimal . It relies on the full precision of 64-bit binary floating-point.

(In fact, in some cases, it relies on the full precision of 80-bit binary floating-point, which is used internally in Intel-compatible CPUs.)

-----
I am wrapping them inside convert decimal function to retain the extra precision I believe as they then are treated as decimal datatype instead of double

That is incorrect. Ironically, your use of CDec works because it reduces the precision, not increases it.

First, the sub-expression a/b is evaluated as type Double because its terms are type Long and type Double.

"a" and "b" are not converted to type Decimal before performing the division.

Second, CDec effectively rounds a type Double argument to 15 significant digits, persumably by converting it to a string.

To demonstrate, note that the exact decimal presentation of binary result of a/b is 499.99999999999994315658113919198513031005859375, which we can approximate by 499.99999999999994, the first 17 significant digits (rounded), with no loss of binary precision.

VBA Code:
Sub doit()
Dim a As Long, b As Double, c As Double
a = 559
b = 1.118
c = a / b
MsgBox (a / b = val("499.99999999999994")) & _
    vbNewLine & a / b & "" & _
    vbNewLine & CDec(a / b) & _
    vbNewLine & CDec("499.99999999999994")
End Sub

displays:
True
500
500
499.99999999999994

-----
1 / 1.118 for example = 0.894454382826476, which is 15 decimal places but double datatype is rounding to 14 decimal places max

That is incorrect.

First, 1/1.118 displays 0.894454382826476 because both Excel and VBA arbitrarily limit formatting to the first 15 significant digits (rounded).

But the actual precision can be much more.

In this case, 1/1.118 is exactly 0.8944543828264757134860474252491258084774017333984375, which can be approximated by 0.89445438282647571 with no loss of binary precision.

Second, neither Excel nor VBA rounds to 14 significant digits automatically.

Finally, the issue has nothing to do with the binary representation of 1/1.118 per se.

Instead, it has to do with the binary representation of 1.118.

1.118 cannot be represented exactly in 64BFP. Instead, it must be approximated in binary by the sum of 53 consecutive powers of 2 ("bits").

Consequently, the exact decimal presentation is 1.1180000000000001048050535246147774159908294677734375.

Since that is infinitesimally larger than 1.118, 559/1.118 is infinitesimally smaller than 500.
 
Upvote 0
You made a number of misstatements
Sorry. I wasn’t trying to answer my own question or make any statements as to the actual problem, they were just my thoughts on what was happening and I thought I’d quantified those as actually unknown to me and not facts.

So I read through your answer but I’m still confused as to what the prescribed way to perform this calculation actually is to get the correct result? I believe ( 1 / 1.118 ) * 559 or 559 / 1.118 is 500, is that not correct?
 
Upvote 0
You made a number of misstatements

Sorry. I wasn’t trying to answer my own question

I'm sorry. You had nothing to apologize for. I think you read something in the "tone" of my words that I did not intend. My mistake!

You seemed to want to understand the technical details. I had hoped that my explanation would help you with that. Too bad if they missed their mark.

-----
I’m still confused as to what the prescribed way to perform this calculation actually is to get the correct result

In summary, the point I was making is: 64-bit binary floating-point arithmetic is messy.

I would suggest that you avoid it by using integer arithmetic.

You wrote: ``I am looping through numbers 1 through 99999. For each number I am dividing it by 1.118. I then want to test if the result is a whole number``.

VBA Code:
Sub doit()
Dim i As Long, x As Double, f As Double
Application.ScreenUpdating = False
For i = 1 To 99999
x = i * 1000 / 1118
f = x - Int(x)
Cells(i, "a") = i
Cells(i, "b") = f
Next
With Range("b1:b99999")
    .NumberFormat = "0.00000000000000000"  ' 17 dp
    .EntireColumn.AutoFit
End With
Application.ScreenUpdating = True
MsgBox "done"
End Sub

1686118364469.png
 
Upvote 0
Sorry for the incessant responses. Apparently the forum does not permit me to edit postings (yet).

.NumberFormat = "0.00000000000000000" ' 17 dp

I chose to format column B as Number with 17 decimal places after I learned that the smallest decimal fraction is 0.00178890876122750.
 
Upvote 0
Again, apologies for the incessant responses. I'm a perfectionist. (sigh)

the point I was making is: 64-bit binary floating-point arithmetic is messy

What I meant is: 64BFP arithmetic with decimal fractions.

Of course, the "integer arithmetic" still uses 64BFP, and the results might be non-integer 64BFP where expected.
 
Upvote 0
Thanks for persisting. OK, so this behaves differently because the input numbers are all integer to begin with and I’m not feeding in a fraction. Could there be a case when thats not possible and how do you know when these errors are then present enough to impact whats coming out at the end. I only spotted this initially as I was checking my code outputted as expected and 559 was my test case as I knew it should be 0.
 
Upvote 0

Forum statistics

Threads
1,216,213
Messages
6,129,552
Members
449,516
Latest member
lukaderanged

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top