VB - Divide integers doesn't include commas? - vba

This is probably an easy one, but I've got this Excel vba macro where I'm trying to divide two integers and I end up with a round number, even though there should be a comma in there.
For example 278 / 101 = 3. While it's really 2,75 and not 3. Why is this?
The macro is really simple, like this:
Dim intFull, intLow , intDivided as Integer
intFull = 278
intLow = 101
intDivided = intFull \ intLow

Your result variables is an integer
If you work with doubles instead you will get 2.752 etc - which can be rounded using dbDivided = Round(lngFull / lngLow, 2)
[variables updated to be more meaningful]
Sub redone()
Dim lngFull As Long
Dim lngLow As Long
Dim dbDivided As Double
lngFull = 278
lngLow = 101
dbDivided = Round(lngFull / lngLow, 2)
End Sub

Sure you used a forward slash and not a backslash? (See also: http://zo-d.com/blog/archives/programming/vba-integer-division-and-mod.html)

Related

Convert integer value to fractional value (decimal)

Looking for a way to convert a positive number (integer) from e.g. 123 to 0.123 without using strings. Can be any size integer. Not concerned about negative values.
Dim value As Integer = 123
Dim num As Decimal = "." & value.ToString
It seems simple but I'm not sure how to do it using math. How can I convert without using strings?
You can get the number of digits using Log10. I think the best way is thusly:
Dim value = 123 'Or whatever other value
Dim digitCount = Math.Floor(Math.Log10(Math.Abs(value))) + 1
Dim result = value * CDec(10 ^ (-digitCount))
You need to use Floor rather than Ceiling in order to get the right result for 0, 10, 100, etc.
Here's one method:
Dim value As Integer = 123
Dim num As Decimal = value
While Math.Abs(num) >= 1
num = num / 10
End While
The Math.Abs takes care of negatives, so you could remove it as you say you're not concerned with them.
This also works:
Dim value as Integer = 123
Dim num as Decimal = value
num /= 10 ^ Math.Ceiling(Math.Log10(value))
It's a little harder to see what's going on initially, but it basically finds the next power of 10 that is above the number, and divides by the 10^(that power)
Edit As per Craigs answer Math.Floor then add one works better than Math.Ceiling thus:
Dim value as Integer = 123
Dim num as Decimal = value
num /= 10 ^ (Math.Floor(Math.Log10(value)) + 1)
Edit
I just did a quick performance comparison, and on my PC doing the While loop took 851ms, and the Log10 method took 158ms. That's for 1,000,000 iterations of each.

VB.NET Xor + Mod

I'm very new to VB.NET language so there are some things I'm still learning and I need some help here. So I'd appreciate any guidance.
I'm making an XOR encryption app where there's a key, input and output. The flow is like this: Key XOR Input and the result will appear as the output. I managed to successfully come out with workable codes for that part though.
However, right now, I need to make a continuation from that part. I need the output to come out within the ASCII range of 33 - 126 (DEC) only.
I have not done anything much in terms of coding as I can't seem to find the right guide. Additionally, I don't really know where to start except that some mathematical logic (MOD) is involved here.
So any pointers? Thank you.
I am using Visual Studio (2017) and here's my code:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim key As String
Dim input As String
Dim output As String
Dim keyCounter As Integer = 0
Dim length As Integer
key = TextBox1.Text
input = TextBox2.Text
length = key.Length
For Each letter As Char In input
output = output & Chr(Asc(letter) Xor Asc(key.Chars(keyCounter)))
If keyCounter = length - 1 Then
keyCounter = 0
Else
keyCounter += 1
End If
Next
TextBox3.Text = output
End Sub
End Class
The math is quite simple. To constrain a number within a range using modulo start by calculating how many numbers there are in the range (inclusive):
126 - 33 + 1 = 94
Then take your value and calculate the modulo using the length, and add the lower value of the range (33) to make it go from 33-126 instead of 0-93:
(value Mod 94) + 33
Alternatively, if you need numbers that already are in the range 33-126 to not change you can subtract the lower value first:
((value - 33) Mod 94) + 33

How to Truncate a Double in VBA

I have a variable in VBA that I need to truncate to 4 significant figures. I can't seem to find anything that won't round the number up or down. But I just want to remove the numbers after the 4th significant figure. I've tried,
compressibility = round(compress, -3 - (Int(Log(Abs(compress)))))
It removes the numbers after the 4th digit but it still rounds the number up.
Compress is a number around 0.000245848385 as an example, and I need the compressibility number to be 0.0002458.
Any suggestions would be great! Thanks.
Try this function:
Function RoundSignificant(ByVal dValue As Double, iFigures As Integer)
Dim dSig As Double
dSig = Abs(dValue)
dSig = Application.Log10(dSig)
dSig = 1 + Int(dSig)
dSig = iFigures - dSig
RoundSignificant = Round(dValue, dSig)
End Function
Sub test()
Debug.Print RoundSignificant(0.000245848385, 4)
End Sub
Using worksheet functions:
=VALUE(TEXT(compress,"0.000E+00"))
For VBA
CDbl(Format(compress,"0.000E+00"))
Hope that helps.
It seems to me that you want to avoid rounding UP, but not rounding down, since rounding down should produce the exact result you want. So, instead of using VBA Round function, you could use Excel WorksheetFunction.RoundDown method to achieve the result you need.
ROUNDDOWN(0.00024586548385;7)=0.000245800000
ROUNDDOWN(0.00024583548385;7)=0.000245800000
Sub test()
Dim compress As Double
compress = 0.000245858
Dim compressibility As Double
compressibility = Int(compress * 10 ^ -(Int(Log(Abs(compress))) - 3)) / 10 ^ -(Int(Log(Abs(compress))) - 3)
Debug.Print compressibility
End Sub

Is it possible to add cases to a Select Case based on the number of entries in a table?

I've been messing around with VBA in Excel a bit recently; and as a small project for myself, I'm trying to create a "draw names from a hat" sort of macro.
I began by generating a random number, and then choosing which entry from a Table (i.e. ListObject) would be selected using a case statement. The problem with this is that it only works of the number of Table entries is always the same.
So my question (probably a ridiculous one) is: is it possible at all to generate a dynamic 'Select Case' block, where the number of cases on the block is based on the number of entries in the Table?
Thanks.
-Sean
Edit: To clarify: what I am trying to do, exactly, is this:
I generate a random number, i, from 1 to n=10*(number of Table entries). After this, I want to display, in a cell, one of the table entries based on the random number.
Ideally, the code would work similarly to this:
if i = 1 to 10 then choose event 1
if i = 11 to 20 then choose event 2
if i = 21 to 30 then choose event 3
...
if i = (n-9) to n then choose event (n/10)
I hope this helps to clarify the goal of the code.
From our comments here is something you can use:
Sub random()
Dim used_rows As Integer
Dim random As Integer
Dim cell_array() As Integer
used_rows = Sheet1.UsedRange.Rows.Count
ReDim cell_array(used_rows)
For i = 1 To used_rows
cell_array(i - 1) = Cells(i, 1)
Next
random = Int(Rnd * (used_rows))
MsgBox cell_array(random)
End Sub
You can go ahead and change MsgBox to whatever you like, or set like Cell(1,4).Value = cell_array(random), or however you'd like to proceed. It will be based off the number of rows used. Though depending on how you implement your spreadsheet the code might have to be changed a bit.
Here's the update code from the suggestions from the comments. Also remember to use Randomize() in your form initialization or WorkBook Open functions.
Sub random()
Dim used_rows As Integer
Dim random As Integer
'Multiple ways to get the row count, this is just a simple one which will work for most implementations
used_rows = Sheet1.UsedRange.Rows.Count
random = Int(Rnd * (used_rows))
'I use the variable only for the reason that you might want to reference it later
MsgBox Cells(random, 1)
End Sub
This assumes that by "table" you mean "Table with a capital T", known in VBA as a ListObject:
Sub PickRandomTens()
Dim lo As Excel.ListObject
Dim ListRowsCount As Long
Dim RandomNumber As Long
Dim ListEvent As String
Dim Tens As Long
Set lo = ActiveSheet.ListObjects(1)
ListRowsCount = lo.DataBodyRange.Rows.Count
RandomNumber = Application.WorksheetFunction.RandBetween(10, ListRowsCount * 10)
ListEvent = lo.ListColumns("Data Column").DataBodyRange.Cells(Int(RandomNumber / 10))
MsgBox "Random number: " & RandomNumber & vbCrLf & _
"Event: " & ListEvent
End Sub

Sum of elements (4) on mulitple lines in 2d array (txt file)

I have a text file that reads:
Left Behind,Lahaye,F,7,11.25
A Tale of Two Cities,Dickens,F,100,8.24
Hang a Thousand Trees with Ribbons,Rinaldi,F,30,16.79
Saffy's Angel,McKay,F,20,8.22
Each Little Bird that Sings,Wiles,F,10,7.70
Abiding in Christ,Murray,N,3,12.20
Bible Prophecy,Lahaye and Hindson,N,5,14.95
Captivating,Eldredge,N,12,16
Growing Deep in the Christian Life,Swindoll,N,11,19.95
Prayers that Heal the Heart,Virkler,N,4,12.00
Grow in Grace,Ferguson,N,3,11.95
The Good and Beautiful God,Smith,N,7,11.75
Victory Over the Darkness,Anderson,N,12,16
The last element of each line is a price. I would like to add up all the prices. I've been searching for so many hours now and cannot find a thing to answer my question. This seems soooo easy but I cannot figure it out!!! Please help out. BTW, this list is bound to change (adding of lines, deletion of lines, altering of lines) so if you can, please nothing concrete but instead leave the code open to changes. Thanks!!!
Just so you can see my pooooorrrr work, here is what I have (I think I deleted my code and rewrote a different way for several hours now.):
Dim Inv() As String = IO.File.ReadAllLines("Books.txt")
Dim t As Integer = Inv.Count - 1
Dim a As Integer = 0 to t
Dim sumtotal As String = sumtotal + Inv(4)
also,
for each line has either an "F" or an "N". how do I add up all the F's and all the N's. Do I do it via if statements?
First, you'll be better off using Double as your type instead of String. Second, observe how I use the Split function on each line, cast its last element as a double, and add it to the total. Yes, using an If Statement is how you can determine whether or not to add to the count of F or the count of N.
Dim lstAllLines As List(Of String) = IO.File.ReadAllLines("Books.txt").ToList()
Dim dblTotal As Double = 0.0
Dim intCountOfF As Integer = 0
Dim intCountOfN As Integer = 0
For Each strLine As String In lstAllLines
Dim lstCells As List(Of String) = strLine.Split(",").ToList()
dblTotal += CDbl(lstCells(3))
If lstCells(2) = "F" Then
intCountOfF += 1
Else
intCountOfN += 1
End If
Next