VBA Division of integers - vba

It seems improbable that this is not a duplicate of a question that already has an answer but I can't find that question or answer so here I go...
In VBA, in the immediate window, if I type:
?8/7
I get the result:
1.14285714285714 which if I then multiply by 7 gives me a number that is slightly LESS than 8, i.e. 7.99999999999998. My pocket calculator provides more decimal places, so it's better than VBA? Right? ;-)
However, if I add 0.000000000000003 to the RESULT 1.142... before I multiply I get 8 (which, by the way, is also incorrect); but my question is:
How can I control the precision of the answer with respect to the number of decimal places shown for ?8/7?
I have seen this answer relating to the accuracy of floating points but it is a different question, it explains the reasons for the inaccuracies whereas I am interested in getting a few extra digits.
To this end I have tried writing a function that returns a double but it does not return enough decimal places... for example I'd like to see: 1.142857142857142857. I also found references to a decimal data type but I think the article is for VB6 (instead of VBA) think I have exhausted the available data types in VBA... what am I missing?
Function test() As Double
Dim a As Double
Dim b As Double
Dim c As Double
a = 8
b = 7
c = a / b
test = c
End Function

Graphing and scientific calculators are not built for speed. They can afford to use software implementation of decimal floats for their calculations rather than IEEE doubles. So it is quite possible that your pocket calculator has greater precision than VBA, but at a cost. What they do is likely to be more than an order of magnitude slower.
VBA lacks a built-in arbitrary precision floating point library, but its decimal type supports higher precision than doubles. Unfortunately, it isn't a full-fledged data type but is rather a subtype of Variant. To create them, you need to use CDec():
Sub test()
Dim x As Variant
x = CDec(8) / 7
Debug.Print x
End Sub
This displays 1.1428571428571428571428571429
When using Decimal, you need to be aware of the fact that if you are not careful, they can pop back to e.g. a Double depending on how you use them:
Sub test2()
Dim x As Variant
x = CDec(x)
Debug.Print TypeName(x)
x = 8 / 7
Debug.Print x
Debug.Print TypeName(x)
End Sub
Output:
Decimal
1.14285714285714
Double

Related

Why does this multiplication cause an OverflowException?

My code tries to multiply 12303 by 174596.
Any decent calculator is capable of providing an answer to this, so why do I get an OverflowException? It also happens when I execute it directly in the Immediate Window.
The code is meant to determine the position of a certain value in a binary file. The file itself is 7 Gb in size.
Is there any way to solve this?
Dim position As Long = hisFileHeader.StreamStartDataPosition +
(TSIdx * hisFileHeader.StreamDataBlockSize)
tsidx has a value of 12303 and StreamDataBlockSize has a value of 174596
I'm guessing that tsidx and StreamDataBlockSize are Integer types. The largest number an Integer type can hold is 2,147,483,647. The multiplication in brackets is then done expecting an integer result, but the answer is out of the range of Integer types. Change your code to ..
Dim position As Long = hisFileHeader.StreamStartDataPosition + (CLng(TSIdx) * hisFileHeader.StreamDataBlockSize)
and the multiplication will be done with the expectation of a Long type.

Need a method for storing obscenely long numbers in number format (not scientific)

How would I go about storing a very large number in number format and not scientific.
Please bear in mind that the number I will be storing is too big for the Long data type.
I've set it as a String.
I have a userform with a command button and a textbox.
Below is the sample code:
Private Sub Cmd_Click()
Dim bits As Integer
Dim out As String
bits = 64
out = 2 ^ (bits - 1)
Txt_Output.Value = out
End Sub
The above will return: 9.22337203685478E+18.
But I want 9223372036854775807.
Can anyone explain how to avoid this?
Thanks in advance.
P.S. I'm hoping to avoid having to use an array.
You can achieve that specific calculation using Decimal data types and a modification to the calculation routine:
Private Sub Cmd_Click()
Dim bits As Integer
Dim out As Variant
Dim i As Long
bits = 64
out = CDec(1)
For i = 1 to bits - 1
out = out * 2
Next
Txt_Output.Value = out
End Sub
By forcing out to be a Variant/Decimal, the calculation does not lose precision as it is being calculated. However some things, such as CDec(2) ^ CDec(63) would still lose precision as the calculation would be done using an intermediate Double precision, so you will need to be very careful as to what calculations you do.
This might give you clues as to how to generalise that method to achieve what you need.
If you have 64-bit Excel, you can use the LongLong data type.

Avoiding overflow with large numbers when using Mod

I began toying with project euler.
I have completed problem #3, but I was only able to complete it by setting my endNumber to 71 * 486847 (manually calculated). When trying to set endNumber = 600851475143 I receive the overflow error.
I even set endNumber as Long, and even Double which I think should be plenty of digits to calculate.
I can't post the whole code here, so I'll post half in hopes someone can show me how to edit it in order to process larger numbers.
Also I noticed that "#" pops up when my code has a very large number like below.
endNumber = 600851475143#
countFactor = 0
For i = 2 To endNumber
If endNumber Mod i = 0 Then
The overflow isn't due to assigning the number 600851475143 to the Double variable it is that the Mod function does not seem to handle numbers longer than Long very well (in fact seemingly not at all).
An alternative can be to use the manual calc for Mod:
endNumber = 600851475143#
countFactor = 0
For i = 2 To endNumber
If endNumber - (Int(endNumber / i) * i) = 0 Then
If you are going to use Mod for large numbers on a regular basis then Chip Pearson's function would be a useful one (which is where I learnt about this subject matter in the first place). It is posted as a problem in Excel but works perfectly in Access.
The highest possible number to work with in a long is: 2.147.483.647
A Double is for decimal purposes, so thats not what you are looking for.
The # that pops up is because a number is to large to be shown inside that cell.
Also I don't know what you want to do with that loop. But that one is going to take a very long while to run.
on this link you can find about handling large numbers: Large Number Arithmetic
Hope that helps

Vb.net + SQL Server : Speed difference using float vs Decimal

I'm using VB.net 2013 and SQL server 2008R2.
I'm writing a financial application , and I'm not sure about which data type to use.
I know that decimal is more precise than float , but I read that using decimal can significantly decrease the speed of calculations , so the speed of my application.
I read that using decimals can be 20 times slower than using float.
Is this true , and if yes is there a solution or should I continue to use float ?
Thank you !
Use decimal for financial applications, period. Speed does not matter in this case. When money is lost, your users won't be happy. You cannot argue saying "well, but on the other hand, it was fast". Regarding 20 times difference on float vs decimal, trust me, you won't feel it at all, there will be more major factors in your app's performance. Most likely trying to synchronize transactions between each other, DB locks etc.
EDIT: Regarding 20 times performance difference, this is true, I was able to reproduce with below code:
Module Module1
Sub Main()
Dim f As Single = 123456792.0F
Dim fsw As New Stopwatch
fsw.Start()
For i = 1 To 100000000
f *= 1.00000012F
Next
fsw.Stop()
Dim dsw As New Stopwatch
dsw.Start()
Dim d As Decimal = 123456792.0F
For i = 1 To 100000000
d *= 1.00000012F
Next
dsw.Stop()
Console.WriteLine(f)
Console.WriteLine("Float (ms): " & fsw.ElapsedMilliseconds)
Console.WriteLine(d)
Console.WriteLine("Decimal (ms): " & dsw.ElapsedMilliseconds)
Console.WriteLine("Float is " & dsw.ElapsedMilliseconds / fsw.ElapsedMilliseconds & " faster")
Console.ReadLine()
End Sub
End Module
Output:
While writing this code, I got anywhere between 10-20 times for different numbers. As I mentioned before, speed is not the concern, compare the accuracy of both approaches, notice how float is off by several orders of magnitude. This is, of course, a synthetic example, but it shows how people may end up with 1 dollar on their payroll instead of a 1000 - imagine the reaction.

Poisson delay formula/function for VB?

I have a requirement where in I need to sleep for a Poisson duration before sending the next packet. The current formula I can think of is
( e^(-lambda) X lambda^t ) / fact(t)
However, for time steps 280 and more the fact(t) would become obsolete due to overflow.
Can someone help me workaround this conventional way in VB .NET?
I think you are looking for the inter-arrival time. A random inter-arrival time can be generated using
t = (Math.log(1.0-Math.random())/-lambda
The formula you posted is the one that defines the probability that there are exactly t (in your case) arrivals within a specific time period.
See the Wikipedia article on generating Poisson distributions.
Even though the factorial value is getting extremely large (as you've observed), the λ k term is also getting somewhat large to compensate. For a way to represent the distribution that takes this into account, see the Wikipedia article on the Poisson distribution:
A VB implementation might look something like:
Module Module1
Sub Main()
Console.WriteLine(Poisson(4, 250))
Console.ReadKey()
End Sub
Function Poisson(ByVal lambda As Integer, ByVal k As Integer) As Double
Poisson = Math.Exp(k * Math.Log(lambda) - lambda - SumOverLn(1, k))
End Function
Function SumOverLn(ByVal start As Integer, ByVal endval As Integer) As Long
Dim i As Integer
SumOverLn = 0
For i = start To endval
SumOverLn = SumOverLn + Math.Log(i)
Next
End Function
End Module
It looks like after a while it is so close to 0 that it registers as such. You may be able to adjust the precision of the display to get more decimal places, but 0 might be a decent enough approximation for high values (the notion that you get from the probability theory seems to be that those values are indeed very close to zero anyway).