vba average calculator userform - vba

Hey im trying to write a small VBA program that calculates the average quiz score the problem is when i enter in the third number it comes back incorrect after doing the first two right.What am i missing?
This is the design view
Option Explicit
Dim total As Double
Dim number As Double
Dim average As Double
Private Sub CommandButton1_Click()
If IsNumeric(TextBox1.Value) = True Then
total = CDbl(average + TextBox1.Value)
number = CDbl(number + 1)
average = CDbl(total / number)
TextBox2.Value = number
TextBox3.Value = average
TextBox1.Value = ""
Else
MsgBox ("please enter a number")
TextBox1.Value = ""
End If
End Sub

Your math is off. If you want to do it, using the previous average, and the new value, then the formula you should use is this:
( [old average] + ( [new value] / [count - 1] ) ) / ( [count] / [count - 1] )
However, as you might see, this is not a very easy-to-read design. I would go for a design, using an array. That way you can keep the math a lot more intuitive, plus you can look back at old score, if you want. The easiest design is probably to keep the array the size of your score count. This requires redim() each time you add a new score. From a performance point-of-view, this is quite bad practice. But in your tiny calculator, thats hardly going to be a problem.

I think you should get number, average, and total value before you do the calculations:
Option Explicit
Dim total As Double
Dim number As Double
Dim average As Double
Private Sub CommandButton1_Click()
number = 0
average = 0
total = 0
If IsNumeric(TextBox1.Value) = True Then
total = CDbl(average + TextBox1.Value)
number = CDbl(number + 1)
'prevent error
If number > 0 then
average = CDbl(total / number)
Else
average = 0
End If
TextBox2.Value = number
TextBox3.Value = average
TextBox1.Value = ""
Else
MsgBox ("please enter a number")
TextBox1.Value = ""
End If
End Sub
Hope this help.

Related

How to Round in VBA

I'm using worksheetFunction.pmt and need the output in the msgbox to be rounded to 2 decimal places or to display just the whole number.
I have declared the variables as Double and tried to add Round into the msgbox output but get a runtime error 13. I can't figure out where to put the Round function
Sub pmt()
Dim Sum, Rate, Period As Double
Sum = InputBox("Enter the sum of the loan")
Rate = InputBox("Enter the interest rate")
Period = InputBox("Enter the number of payments")
MsgBox "Your PMT is: $ " & WorksheetFunction.pmt(Rate, Period, -Sum) & ""
End Sub
I expect the result to return only 2 decimal places. Is it possible to round the worksheetFunction.pmt
Just use the VBA Round function:
Sub pmt()
'Take care when naming your variables
'Dim dlbSum as Double, dblRate as Double, dblPeriod as Double
'Defining Period as Dboule doesn't make sense anyway
Dim Sum, Rate, Period As Double
Sum = InputBox("Enter the sum of the loan")
'You should consider error checking, entering the percentage symbol will create an error in the calculations
Rate = InputBox("Enter the interest rate")
Period = InputBox("Enter the number of payments")
MsgBox "Your PMT is: $ " & Round(WorksheetFunction.pmt(Rate, Period, -Sum), 2) & ""
End Sub

Unconventional rounding of numbers - not all numbers work

I'm adding two decimal numbers. Whenever the fractional part gets to 0.60 it should be rounded up, for example 20.60 is rounded up to 21.00.
I've been able to do that, and the application is mostly working, but whenever the number before the decimal point exceeds the hundreds column, lets say 999.40, and it gets to the thousands column, the thousands separator doesn't work. For example, if I want to add two numbers, say 600.20 and 600.30, instead of my answer being 1,200.50 I get 1.00.
This is my code so far:
Private Sub Calculate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Calculate.Click
'ans for txtbox5.tetx
Try
If Convert.ToInt32(Microsoft.VisualBasic.Right((Val(Label41.Text).ToString("N2")), 2)) + Convert.ToInt32(Microsoft.VisualBasic.Right((Val(Label42.Text).ToString("N2")), 2)) > 99 Then
MessageBox.Show("invalid entry")
Else
Label41.Text = Val(TxtBox2.Text)
Label42.Text = Val(TxtBox34.Text)
'sum of numbers in two txtbox
'TxtBox5.Text = Val(TxtBox2.Text) + Val(TxtBox34.Text)
Label43.Text = (Val(Label41.Text) + Val(Label42.Text)).ToString("N2")
'strip last 2 decimals :
ln = Convert.ToInt32(Microsoft.VisualBasic.Right((Val(Label43.Text).ToString("N2")), 2))
Label44.Text = ln.ToString
'form decimal from 2 decimals
Label45.Text = "0." & Label44.Text
'subtract new decimal from 1st answer
Label46.Text = (Val(Label43.Text) - Val(Label45.Text)).ToString("N2")
'checks if striped decimal is btw 100 and 59
If (Val(Label44.Text)) < 100 And (Val(Label44.Text)) > 59 Then
runup = runup + 1
newans = (Val(Label44.Text) - Val(60))
Label45.Text = (Val(Label45.Text) - Val(0.6)).ToString("N2")
Try
'check if decimal is between 100 and 59
If (Val(newans)) < 100 And (Val(newans)) > 59 Then
runup = runup + 1
newans = (Val(newans) - Val(60))
Label45.Text = (Val(Label45.Text) - Val(0.6)).ToString("N2")
Label47.Text = (Val(runup) + Val(Label46.Text)) + Val(Label45.Text).ToString("N2")
runup = 0
'check if new decimal is between 60 and 0
ElseIf (Val(newans)) < 60 And (Val(newans)) >= 0 Then
Label47.Text = ((Val(runup) + Val(Label46.Text)) + Val(Label45.Text)).ToString("N2")
runup = 0
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
'check if striped decimal is btw 60 and 0
ElseIf (Val(Label44.Text)) < 60 And (Val(Label44.Text)) >= 0 Then
Label47.Text = ((Val(runup) + Val(Label46.Text)) + Val(Label45.Text)).ToString("N2")
runup = 0
End If
End If
Catch ex As Exception
End Try
TxtBox5.Text = Label47.Text
What is causing the problem with the larger numbers?
It looks like you are making your code harder to understand by using UI elements (e.g. labels) as if they were variables. This is not helped by the names of those labels - how are you meant to figure out what, say, "Label44" represents?
You can take the input data from the textboxes and parse that into a suitable data type - I recommend Decimal for this particular case - and using suitable variable names will make it easier to see what the code is doing.
As an example, I put three textboxes on a form and named them "tbNum1", "tbNum2", and "tbSum", added a button named "bnCalculate", and used this code:
Private Sub bnCalculate_Click(sender As Object, e As EventArgs) Handles bnCalculate.Click
Dim num1 As Decimal = Decimal.Parse(tbNum1.Text)
Dim num2 As Decimal = Decimal.Parse(tbNum2.Text)
Dim sum = num1 + num2
Dim frac = sum - Math.Floor(sum)
If frac >= 0.6 Then
sum = Math.Ceiling(sum)
End If
tbSum.Text = sum.ToString("N2")
End Sub
The code would need to be modified to work correctly for negative numbers.
I could not discern what some of your code was intended to do, e.g. is the first check intended to restrict the number of decimal places entered? There are cleaner ways to do that.
N.B. Do not use Try...Catch with an empty Catch part because it will hide problems from you.

Excel VBA - Select multiple values to search a Case

I wrote a program that totals the number of times X number of widgets are tested in a day based upon a start and end count. After a period of time a widget will fail and have to be replaced, thus the count will start back at zero. I am using a Select Case to compute the data and a drop-down menu in Excel to select the widget(s). Everything works great besides one thing... I can't select multiple widgets to search the Case.
I understand the general principles of the Case Statement - but is there any way around searching for only one scenario via a Case?
'Create subroutine that will copy and total data from worksheet 1 to worksheet 2
Private Sub VTS()
'Establish variable for CASE to search
Dim ValR As String
'Establish counter array
Dim myarray(1 To 170)
myarray(1) = Worksheets(2).Range("A7").Value
myarray(2) = Worksheets(2).Range("A10").Value
...
ValR = Worksheets(1).Range("B4").Value
Select Case ValR
Case "1A"
Worksheets(2).Range("C7").Copy ' Copy current Total
Worksheets(2).Range("A7").PasteSpecial ' Move to "Previous Total" to sum total
myarray(1) = Worksheets(1).Range("B3").Value - Worksheets(1).Range("B2").Value
If myarray(1) < 0 Then
myarray(1) = 1000000 + myarray(1)
End If
Worksheets(2).Range("B7").Value = myarray(1)
Worksheets(2).Range("C7").Value = Worksheets(2).Range("A7").Value + Worksheets(2).Range("B7").Value
Worksheets(2).Range("C7").Copy
Worksheets(1).Range("B10").PasteSpecial
Case "1B"
Worksheets(2).Range("C10").Copy
Worksheets(2).Range("A10").PasteSpecial
myarray(2) = Worksheets(1).Range("B3").Value - Worksheets(1).Range("B2").Value
If myarray(2) < 0 Then
myarray(2) = 1000000 + myarray(2)
End If
Worksheets(2).Range("B10").Value = myarray(2)
Worksheets(2).Range("C10").Value = Worksheets(2).Range("A10").Value + Worksheets(2).Range("B10").Value
Worksheets(2).Range("C10").Copy
Worksheets(1).Range("B10").PasteSpecial
Case Else
MsgBox "Wrong Model Entered / Model Does Not Exist"
End Select
End Sub
Any suggestions?
THANKS!
You can have the user separate widgets by ";" and then use the following loop:
'widgetString = "widget1;widget2;widget3"
widgets = Split(widgetString, ";")
for w = 0 to ubound(widgets)
thisWidget = widgets(w)
'place all of your code here, with thisWidget being the current widget being evaluated
next w

Is there a way to put bounds on Goal Seek? If not, how would you go about this?

I'm trying to minimize the value of the sum of the residuals squared by varying the value of De, which is found in F1. I want the values of CFL Calculated to be as close as possible to the values of CFL Measured. The smaller the sum of those residuals squared, the better the fit! After asking stackoverflow for some advice, I decided to use Goal Seek to minimize the sum of the residuals squared to get as close to zero as possible by varying the value of De, which I want to find the most ideal value of.
I got this program to run perfectly, or so I thought... I found out that instead of summing every single residuals using =SUM(D2:D14), I accidentally used =SUM(D2,D14). So I was only summing up the first and last numbers.
Now that I'm trying to sum every residual squared up, I'm getting these crazy errors, and an insane value for De.
I know that the value of De has to be greater than zero, and less than one. how can I use these bounds to keep this goal seek focused within a certain range? The answer for De in this case is about .012, if that helps. I keep getting the error #NUM! in all of the residual cells. Is this because of overflow issues?
If you've concluded that using Goal Seek to minimize these sums by finding the most ideal value of De will not work, how would you go about it? Are there any other solvers I could use?
Here is the code:
Option Explicit
Dim Counter As Long
Dim DeSimpleFinal As Double
Dim simpletime As Variant
Dim Tracker As Double
Dim StepAmount As Double
Dim Volume As Double
Dim SurfArea As Double
Dim pi As Double
Dim FinalTime As Variant
Dim i As Variant
Sub SimpleDeCalculationNEW()
'This is so you can have the data and the table I'm working with!
Counter = 13
Volume = 12.271846
SurfArea = 19.634954
pi = 4 * Atn(1)
Range("A1") = "Time(days)"
Range("B1") = "CFL(measured)"
Range("A2").Value = 0.083
Range("A3").Value = 0.292
Range("A4").Value = 1
Range("A5").Value = 2
Range("A6").Value = 3
Range("A7").Value = 4
Range("A8").Value = 5
Range("A9").Value = 6
Range("A10").Value = 7
Range("A11").Value = 8
Range("A12").Value = 9
Range("A13").Value = 10
Range("A14").Value = 11
Range("B2").Value = 0.0612
Range("B3").Value = 0.119
Range("B4").Value = 0.223
Range("B5").Value = 0.306
Range("B6").Value = 0.361
Range("B7").Value = 0.401
Range("B8").Value = 0.435
Range("B9").Value = 0.459
Range("B10").Value = 0.484
Range("B11").Value = 0.505
Range("B12").Value = 0.523
Range("B13").Value = 0.539
Range("B14").Value = 0.554
Range("H2").Value = Volume
Range("H1").Value = SurfArea
Range("C1") = "CFL Calculated"
Range("D1") = "Residual Squared"
Range("E1") = "De value"
Range("F1").Value = 0.1
'Inserting Equations
Range("C2") = "=((2 * $H$1) / $H$2) * SQRT(($F$1 * A2) / PI())"
Range("C2").Select
Selection.AutoFill Destination:=Range("C2:C" & Counter + 1), Type:=xlFillDefault
Range("D2") = "=((ABS(B2-C2))^2)"
Range("D2").Select
Selection.AutoFill Destination:=Range("D2:D" & Counter + 1), Type:=xlFillDefault
'Summing up the residuals squared
Range("D" & Counter + 2) = "=Sum(D2: D" & Counter + 1 & ")"
'Goal Seek
Range("D" & Counter + 2).GoalSeek Goal:=0, ChangingCell:=Range("F1")
Columns("A:Z").EntireColumn.EntireColumn.AutoFit
DeSimpleFinal = Range("F1")
MsgBox ("The Final Value for DeSimple is: " & DeSimpleFinal)
End Sub
You're getting NUM errors because the value of F1 is going negative in your current solution -- and you are trying to take the square root of F1 in one of your expressions.
Also, goal seek is, in this instance, incredibly sensitive to the particular initial starting "guess" for F1 that you are using. This will be evident if you vary the F1 initial value by a little bit on either side of the 0.1 you are using now. There are, in fact, large regions of instability in the goal seek solution, depending on the F1 value:
As you brought up in your question, you are more likely to get a useable result if you can set constraints on the possible inputs to your solution search. Excel comes with an add-in called Solver that allows that, as well as offers several different search methods. Solver is not loaded automatically when you first start Excel, but loading it is easy, as explained here.
You ask for other solvers. For alternatives and a bit of theory to help understand what's going on, have a look at Numerical Recipes (online books here). Chapter 10 deals with this. It includes ready-made code samples if you want to try something different than GoalSeek or the Solver add-in. Of course the code is in Fortran/C/C++ but these are readily translated into VBA (I've done this many times).
The goalseek function uses a dichotomy algorithm which can be coded like this:
Sub dicho(ByRef target As Range, ByRef modif As Range, ByVal targetvalue As Double, ByVal a As Double, ByVal b As Double)
Dim i As Integer
Dim imax As Integer
Dim eps As Double
eps = 0.01
imax = 10
i = 0
While Abs(target.Value - targetvalue) / Abs(targetvalue) > eps And i < imax
modif.Value = (a + b) / 2
If target.Value - targetvalue > 0 Then
a = (a + b) / 2
Else
b = (a + b) / 2
End If
i = i + 1
Wend
End Sub
Where a and b are you bounds.

Unexpected String Results

I have the following code to check values entered into two input boxes, if both values are zero then the MsgBox should display "Stop!" (I will change this later to exiting the sub but I am using a MsgBox for testing)
From testing I've seen these results:
A zero in both strings produces the expected message box.
A non zero in the first string followed by any non zero value in the second string does nothing (as expected).
A zero in the first string followed by a second string value equal to or greater than 10 produces the message box (unexpected).
I've also noticed that if the second string is 6-9 it is displayed as x.00000000000001%. I think this is a floating point issue and could be related? This behaviour occurs without the IF... InStr function too.
Option Explicit
Sub Models()
Dim MinPer As String, MaxPer As String, Frmula As String
Dim Data As Worksheet, Results As Worksheet
Set Data = Sheets("Data")
Set Results = Sheets("Results")
Application.ScreenUpdating = False
MinPer = 1 - InputBox("Enter Minimum Threshold Percentage, do not include the % symbol", _
"Minimum?") / 100
MaxPer = 1 + InputBox("Enter Maximum Threshold Percentage, do not include the % symbol", _
"Maximum?") / 100
If (InStr(MinPer, "0") = 0) And (InStr(MaxPer, "0") = 0) Then
MsgBox "STOP!"
End If
' Remainder of code...
This is the most interesting problem I've come across so far in VBA and welcome any discussion about it.
Edit: I use this code to display on screen the paramaters for the end-user to see. Hence how I noticed the .00000000001% issue:
.Range("D2").Value = "Min is " & 100 - MinPer * 100 & "%"
.Range("D3").Value = "Max is " & MaxPer * 100 - 100 & "%"
Two things
1) Declare MinPer, MaxPer as Long or a Double and not a String as you are storing outputs from a calculation
2) Don't directly use the InputBox in the calculations. Store them in a variable and then if the input is valid then use them in the calculation
Dim MinPer As Double, MaxPer As Double, Frmula As String
Dim Data As Worksheet, Results As Worksheet
Dim n1 As Long, n2 As Long
Set Data = Sheets("Data")
Set Results = Sheets("Results")
Application.ScreenUpdating = False
On Error Resume Next
n1 = Application.InputBox(Prompt:="Enter Minimum Threshold Percentage, do not include the % symbol", _
Title:="Minimum?", Type:=1)
On Error GoTo 0
If n1 = False Then
MsgBox "User cancelled"
Exit Sub
End If
On Error Resume Next
n2 = Application.InputBox(Prompt:="Enter Maximum Threshold Percentage, do not include the % symbol", _
Title:="Maximum?", Type:=1)
On Error GoTo 0
If n2 = False Then
MsgBox "User cancelled"
Exit Sub
End If
If n1 = 0 And n2 = 0 Then
MsgBox "STOP!"
End If
MinPer = 1 - (Val(n1) / 100)
MaxPer = 1 + (Val(n2) / 100)
This is because the number "10" has a "0" in the string (second character) so both evaluate to true.
Try this instead:
If (MinPer = "0") And (MaxPer = "0") Then
MsgBox "STOP!"
End If
For additional control save the user input (MinPer , MaxPer) and THEN text them for validity before performing nay mathematical operations on them.
InStr(MinPer, "0") is just checking to see whether the string contains a zero
character.
You need to convert the string value to an integer. Use the IsNumeric and CInt functions
to do that. See this URL:
vba convert string to int if string is a number
Dim minPerINT as Integer
Dim maxPerINT as Integer
If IsNumeric(minPer) Then
minPerINT = CInt(minPer)
Else
minPerINT = 0
End If
If IsNumeric(maxPer) Then
maxPerINT = CInt(maxPer)
Else
maxPerINT = 0
End If
If minPerINT = 0 and maxPerINT=0 Then
MsgBox "STOP!"
End If
Depending on what data can be entered It may also be a good idea to check if the length
of the data is zero using the len() function.