VBA - random number between two values - excel

I've seen multiple answers say the following algorithm works fine to generate a random number between two values. I'm getting spurious results where I'll sometimes get a value returned that is higher than the upper bound.
Dim random as Integer
random = Int (3 - 0 + 1) * Rnd + 0
Debug.Print random
This should give values between 0 - 3 right? I'm seeing 0 to 4 when run a few times..???

This is one of the idiosyncrasies of VBA.
You can see the effect more clearly by writing
random = Int(4) * Rnd
4 * Rnd is a floating point double type, and when it gets assigned to random, The same rounding convention as for CInt is applied1; i.e. if Int(4) * Rnd is 3.5 or greater, the result is 4.
The fix is to write
random = Int(4 * Rnd)
1 The convention "round half to even" is often called Banker's Rounding. See
https://en.wikipedia.org/wiki/Rounding#Round_half_to_even

I think it is to be expected when explicitly converting floating point number to integer, but what might not be expected is that the rounding is towards the closest even number:
Dim i As Integer
i = 3.5 ' i = 4
i = "2.5" ' i = 2

Related

Mod calculation in VBA

why vba shows answer "0" when Google shows "11.05"?
Sub test2()
Debug.Print 22.15 Mod 11.1
End Sub
Q: Is it possible to get in VBA result the same as Google provide?
UPD2:VBA's Mod operator (not function) differs significantly from Excels MOD function is a few respects. First, it handles negative values differently... second, if you use 1 as the divisor, it will return 0, not the decimal portion of the floating point number... third, it handles floating point number differently (it uses Banker's Rounding to round all floating point number to whole numbers before performing it operation on those numbers whereas Excel doesn't).
As Mod in VBA only deals with integers, you'll have to scale your values, for example with 10 ^ 3 (1000) if you have values of three decimals or less:
DecimalCount = 3
Scaling = 10 ^ DecimalCount
Debug.Print (22.15 * Scaling Mod 11.1 * Scaling) / Scaling
11.05
Debug.Print (22.15 * Scaling Mod 11.075 * Scaling) / Scaling
0
Just adjust DecimalCount to match your expected values.
An alternative to Gustav's answer that doesn't need the scaling: First make an regular division, use the integer part of the result (integer quotient) and subtract the divisor times the (integer) quotient from the dividend.
Function ModDouble(a As Double, b As Double) As Double
Dim y As Double
y = (a / b)
ModDouble = a - (b * Int(y))
End Function
Testing it:
Sub test()
Debug.Print ModDouble(5, 3)
Debug.Print ModDouble(22.15, 11.1)
End Sub
> 2
> 11.05

Log function in excel (vba) - not giving me the right answer

In this code snippet below, the value of temp_int2 is 1, makes no sense to me. --> log(1000) = 3, that is log of base 10, the log function is base 'e'.
im not sure if its the "INT" function which problematic but could someone please assist.
temp_int2 = Int(Log(1000) / Log(10)) - 1
'temp_int2 = Int(Log(cap_dec) / Log(10)) - 1
MsgBox ("Value of log functuon -->" & CStr(Log(cap_dec) / Log(10)) & " value after log function " & CStr(temp_int2))
Instead of the Int function use the cLng function. While Int will cut off decimals, cLng will round to a Long.
Example Int will cut off
Int(99.2) '= 99
Int(99.5) '= 99
Int(99.8) '= 99
but cLng will round
cLng(99.2) '= 99
cLng(99.5) '= 100
cLng(99.8) '= 100
since computers and calculators calculate numeric and not algebraic there is probably a precision issue in calculation and Log(1000) / Log(10) is not exactly an algebraic 3 but a numeric 3 that is something like 2.99999999999998 which Int will cut off to 2 but cLng will round to 3.
Note that Excel is a numeric calculation program and values of type Double are not exact values. The decimals are (as with any standard calculator too) only calculated up to a defined precision.
So a Double type 3 is not a 3 but something very close to 3 like 2.99999999999998. So the Log function returns a Double and also a devision Log(1000) / Log(10) returns a Double and this is not exactly 3 but very close to 3.
Note that this is not a bug but in the nature of numeric calculations which are never exact but only precise, while algebraic calculations can be exact.
The same problem occurs when comparing values of type Double:
If DoubleA = DoubleB Then 'might not work
Here you need to use something like
If (DoubleA - DoubleB) ^ 2 < (10^ - Digits)^2
where Digits is the number of digits that need to be the same. Example
DoubleA = 0.9999999999
DoubleB = 1.0000000001
then Digits needs to be <= 9 to consider them as equal.
If you need to do that often then it comes handy to use a function for that:
Option Explicit
Public Function IsDoubleValueTheSame(DoubleA As Double, DoubleB As Double, Optional Digits As Long = 12) As Boolean
IsDoubleValueTheSame = (DoubleA - DoubleB) ^ 2 < (10 ^ -Digits) ^ 2
End Function
and call it like
Debug.Print IsDoubleValueTheSame(0.9999999999, 1.0000000001, 9) 'true
Debug.Print IsDoubleValueTheSame(0.9999999999, 1.0000000001, 10) 'false
So to come back to your initial example:
Debug.Print Log(1000) / Log(10) = 3 'false
Debug.Print IsDoubleValueTheSame(Log(1000) / Log(10), 3, 15) 'true
Debug.Print IsDoubleValueTheSame(Log(1000) / Log(10), 3, 16) 'false
which means Log(1000) / Log(10) is actually 3 precise up to 15 digits and the 16ᵗʰ digit is different.
Further information about this:
Numeric precision in Microsoft_Excel (Wikipedia)
Floating-point arithmetic may give inaccurate results in Excel (Microsoft documentation)
Compare double in VBA precision problem (Stack Overflow)

Understanding the maths

I am trying to understand the maths in this code that converts binary to decimal. I was wondering if anyone could break it down so that I can see the working of a conversion. Sorry if this is too newb, but I've been searching for an explanation for hours and can't find one that explains it sufficently.
I know the conversion is decimal*2 + int(digit) but I still can't break it down to understand exaclty how it's converting to decimal
binary = input('enter a number: ')
decimal = 0
for digit in binary:
decimal= decimal*2 + int(digit)
print(decimal)
Here's example with small binary number 10 (which is 2 in decimal number)
binary = 10
for digit in binary:
decimal= decimal*2 + int(digit)
For for loop will take 1 from binary number which is at first place.
digit = 1 for 1st iteration.
It will overwrite the value of decimal which is initially 0.
decimal = 0*2 + 1 = 1
For the 2nd iteration digit= 0.
It will again calculate the value of decimal like below:
decimal = 1*2 + 0 = 2
So your decimal number is 2.
You can refer this for binary to decimal conversion
The for loop and syntax are hiding a larger pattern. First, consider the same base-10 numbers we use in everyday life. One way of representing the number 237 is 200 + 30 + 7. Breaking it down further, we get 2*10^2 + 3*10^1 + 7*10^0 (note that ** is the exponent operator in Python, but ^ is used nearly everywhere else in the world).
There's this pattern of exponents and coefficients with respect to the base 10. The exponents are 2, 1, and 0 for our example, and we can represent fractions with negative exponents. The coefficients 2, 3, and 7 are the same as from the number 237 that we started with.
It winds up being the case that you can do this uniquely for any base. I.e., every real number has a unique representation in base 10, base 2, and any other base you want to work in. In base 2, the exact same pattern emerges, but all the 10s are replaced with 2s. E.g., in binary consider 101. This is the same as 1*2^2 + 0*2^1 + 1*2^0, or just 5 in base-10.
What the algorithm you have does is make that a little more efficient. It's pretty wasteful to compute 2^20, 2^19, 2^18, and so on when you're basically doing the same operations in each of those cases. With our same binary example of 101, they've re-written it as (1 *2+0)*2+1. Notice that if you distribute the second 2 into the parenthesis, you get the same representation we started with.
What if we had a larger binary number, say 11001? Well, the same trick still works. (((1 *2+1 )*2+0)*2+0)*2+1.
With that last example, what is your algorithm doing? It's first computing (1 *2+1 ). On the next loop, it takes that number and multiplies it by 2 and adds the next digit to get ((1 *2+1 )*2+0), and so on. After just two more iterations your entire decimal number has been computed.
Effectively, what this is doing is taking each binary digit and multiplying it by 2^n where n is the place of that digit, and then summing them up. The confusion comes due to this being done almost in reverse, let's step through an example:
binary = "11100"
So first it takes the digit '1' and adds it on to 0 * 2 = 0, so we
have digit = '1'.
Next take the second digit '1' and add it to 1* 2 =
2, digit = '1' + '1'*2.
Same again, with digit = '1' + '1'*2 +
'1'*2^2.
Then the 2 zeros add nothing, but double the result twice,
so finally, digit = '0' + '0'*2 + '1'*2^2 + '1'*2^3 + '1'*2^4 = 28
(I've left quotes around digits to show where they are)
As you can see, the end result in this format is a pretty simple binary to decimal conversion.
I hope this helped you understand a bit :)
I will try to explain the logic :
Consider a binary number 11001010. When looping in Python, the first digit 1 comes in first and so on.
To convert it to decimal, we will multiply it with 2^7 and do this till 0 multiplied by 2^0.
And then we will add(sum) them.
Here we are adding whenever a digit is taken and then will multiply by 2 till the end of loop. For example, 1*(2^7) is performed here as decimal=0(decimal) +1, and then multiplied by 2, 7 times. When the next digit(1) comes in the second iteration, it is added as decimal = 1(decimal) *2 + 1(digit). During the third iteration of the loop, decimal = 3(decimal)*2 + 0(digit)
3*2 = (2+1)*2 = (first_digit) 1*2*2 + (seconds_digit) 1*2.
It continues so on for all the digits.

Generate random numbers between 0.001359 and 1

I was doing a problem and using the function rnd() but the random values <0.001359 were making other values not acceptable.
How to generate random numbers beginning at 0.001359 until 1?
This will give you a random number greater than or equal to 0.001359 and less than 1.
0.001359 + Rnd() * 0.998641
Try to generate numbers between 1359 and 1000000, and then divide the result by 1000000.
The command for generating the random numbers will be
Int ((1000000 - 1359 + 1) * Rnd + 1359)
For more information look here.
Try this formula (Excel 2013):
=(RANDBETWEEN(1359,1000000)) / 1000000
Since it only returns whole numbers, just slide the decimal as needed and then divide the result to get back to you decimal.
To do that in VBA, you can use this:
randomnumber = WorksheetFunction.RandBetween(1359, 1000000) / 1000000

Rounding error when using INT function

I have user input in two cells, named "UpperRangeHigh" and "UpperRangeLow". I have the following code:
dRangeUpper = [UpperRangeHigh] - [UpperRangeLow]
lLines = Int(dRangeUpper * 100 / lInterval)
The user inputs 120.3 and 120 into the input cells respectively. lInterval has the value 10. VBA produces the result of 2 for lLines, instead of 3.
I can overcome this problem by adding 0.000000001 to dRangeUpper, but I'm wondering if there is a known reason for this behaviour?
This appears to be a problem with Excel's calculation and significant digits. If you do:
=120.3 - 120 and format the cell to display 15 decimal places, the result appears as:
0.2999999999999970
Here is a brief overview which explains how Excel uses binary arithmetic and that this may result in results divergent from what you would expect:
http://excel.tips.net/T008143_Avoiding_Rounding_Errors_in_Formula_Results.html
You can overcome this by forcing a rounded precision, e.g., to 10 decimal places:
lLines = Int(Round(dRangeUpper, 10) * 100 / lInterval
Kindly use single or double when working with decimals to get more accurate results.
Sub sample()
Dim dRangeUpper As Double
dRangeUpper = CDbl("120.3") - CDbl("120")
lLines = Round(CDbl(dRangeUpper * 100 / 10), 4)
End Sub
output = 3
This is a known Floating point issue within Excel
http://support.microsoft.com/kb/78113
From MSDN:
To minimize any effects of floating point arithmetic storage
inaccuracy, use the Round() function to round numbers to the number of
decimal places that is required by your calculation. For example, if
you are working with currency, you would likely round to 2 decimal
places:
=ROUND(1*(0.5-0.4-0.1),2)
In your case, using round() instead of INT should do the trick using 0 rather than 2

Resources