Unconventional rounding of numbers - not all numbers work - vb.net

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.

Related

Math expression parsing: incorrect output when a + followed by a -

I am trying to create a program that parses an expression such as 3x^3 + 7x^2 + 6x - 9. I am using a recursive system to do this. At the moment, I'm just testing to see if I get the correct output for the expression I input by only using constants in the expression. The code works fine with most expressions, but when I have a minus followed by a positive, both terms are subtracted (first term should be subtracted, second term should be added). An example of this is entering 4*3^2-7*3+5*3. The answer to this is 30 but the program does 4*3^2-7*3-5*3 instead and outputs 0. I am unsure how to solve this.
Code:
Private Function ParseExpr(ByRef expression As String)
Dim op, op1 As Integer
op = ParseFactor(expression)
If expression.Length <> 0 Then
If (expression(0) = "+") Then
expression = expression.Substring(1, expression.Length - 1)
op1 = ParseExpr(expression)
op += op1
ElseIf (expression(0) = "-") Then
expression = expression.Substring(1, expression.Length - 1)
op1 = ParseExpr(expression)
op -= op1
End If
End If
Return op
End Function
All is well with raising to a power and multiplication. At that point we are simplified to 36-21+15. Here we must remember that we are adding -21. So we need to go back to -7 . When I build my list of numbers (it handles numbers of more than one digit) I add the minus sign to the number it precedes.
This code does not handle decimal numbers or parenthesis. I think you will be able to add the division operator, if you wish.
Private NumList As New List(Of Double)
Private OperatorList As New List(Of String)
Private Sub OpCode()
Dim Input = "14*3^2-7*3+15*3"
PopulateLists(Input)
Dim OpIndex As Integer
Dim NewNum As Double
Dim operators = {"^", "*", "+"} 'Note: no minus sign, the minus goes with the number
For Each op In operators
Do
OpIndex = OperatorList.IndexOf(op)
If OpIndex = -1 Then
Exit Do
End If
Select Case op
Case "^"
NewNum = NumList(OpIndex) ^ NumList(OpIndex + 1)
Case "*"
NewNum = NumList(OpIndex) * NumList(OpIndex + 1)
Case "+"
NewNum = NumList(OpIndex) + NumList(OpIndex + 1)
End Select
NumList.RemoveAt(OpIndex + 1)
NumList(OpIndex) = NewNum
OperatorList.RemoveAt(OpIndex)
Loop
Next
MessageBox.Show(NumList(0).ToString) 'Displays 150
End Sub
Private Sub PopulateLists(Input As String)
Dim strNum As String = ""
For Each c As Char In Input 'Be careful here, the IDE wants to add () at the end of this line - it doesn't belong
If Char.IsDigit(c) Then
strNum &= c
ElseIf c = "-" Then
OperatorList.Add("+") 'We are adding a negative number
NumList.Add(CDbl(strNum)) 'Add the last number we accumulated so we can start a new one with the minus sign
strNum = "-" 'Start a new number with the minus sign
Else 'The other operators are added to the list
OperatorList.Add(c)
NumList.Add(CDbl(strNum))
strNum = ""
End If
Next
NumList.Add(CInt(strNum)) 'The last number which will not be followed by an operator
End Sub

Visual Basic: Simple Counter with leading Zero

I am trying to create a simple counter that increases when the button is clicked.
What I would like is when the counter is clicked it displays "01", "02" etc.
I can create it already with "1", "2", but I would like to have a leading zero.
I have searched and found I can do this by converting the label to a string, but I cant seem to get the value to count?
If I change "count.text = counter" to "count.text = cot" it will display "01", but wont count. I'm guessing this is due to the fact its only displaying what is currently in the string but not increasing the value?
If I could get any guidance that would be great!
Many thanks!
Dim counter As Integer = 1
Dim cot As String = String.Format("{0:00}", counter)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
counter = cot + 1
count.Text = counter
End Sub
PadLeft is the key.
Dim number as Integer = 12
Console.WriteLine(number.ToString().PadLeft(2, "0")). ' prints 12
number = 2
Console.WriteLine(number.ToString().PadLeft(2, "0")). ' prints 02
The problem is, that you don't update your formatted number properly. It's only initialized a single time.
To increment the counter, use counter += 1 or counter = counter + 1 first. This will add 1 to the current value of the integer variable. Then modify the text of your Label by calling that formatting code again: count.Text = String.Format("{0:00}", counter).
This should get you started...
it converts the string into an integer. increments the number, converts it back into a string and then checks for a leading 0, if not found it adds it.
I will let you convert it into your button click for practise, as it should help you have a good understanding of the conversion between types :)
Sub string_counter()
Dim string_counter As String
string_counter = "00"
For x = 0 To 10
Debug.Print string_counter
string_counter = Int(string_counter) + 1
string_counter = Str(string_counter)
If Left(Trim(string_counter), 1) <> "0" Then
string_counter = "0" + Trim(string_counter)
End If
Next x
End Sub

Why is this basic number comparison producing an illogical result?

While hunting a weird bug in my VB.NET application, I tracked it down to a shockingly puzzling detail. Here is a simple test code:
If 0.01 > 0.12 - 0.11 Then Debug.Print("what the hell")
0.12-0.11 is 0.01... Which is equal to the left side of the comparison. However, when I run this, the debug prints "what the hell"... Because seriously, what the hell. These numbers are equal.
Additionally, if I have a cycle like this:
Dim count As Integer = 0
For i As Double = 0.11 to 0.12 Step 0.01
count += 1
Next
Debug.Print(count)
It prints 1, meaning the cycle is executed only once, while it should execute twice.
Surprisingly, if I change 0.11, 0.12 and 0.01 in the above examples to 0.1, 0.2 and 0.1, then first example doesn't print anything, and the second example prints 2, as it should.
What is going on here? Am I missing something incredibly obvious, or is this some kind of floating point error or something?
You're getting these problems because you're using floating-point types, which use base 2, and base 2 can't represent some fractional values exactly.
That's why fixed-point types like Decimal were devised. If your example code is reworked for fixed-point (using Decimal), it gives the expected results.
' Specify Decimal constants and this will worked as anticipated.
If 0.01D > 0.12D - 0.11D Then Debug.Print("what the hell")
' 0.12-0.11 Is 0.01... Which Is equal to the left side of the comparison.
' However, when I run this, the debug prints "what the hell"... Because
' seriously, what the hell. These numbers are equal.
' Additionally, If I have a cycle Like this
Dim count As Integer = 0
For i As Decimal = 0.11D To 0.12D Step 0.01D
count += 1
Next
Debug.Print(count) ' Prints 2
How about Integer arithmetic?
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim count As Integer = 0
For i As Integer = CInt(0.11 * 100) To CInt(0.12 * 100) Step CInt(0.01 * 100)
count += 1
Next
Debug.Print(count.ToString)
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
If CInt(0.01 * 100) > CInt(0.12 * 100) - CInt(0.11 * 100) Then
Debug.Print("what the hell")
Else
Debug.Print("It's Ok")
End If
End Sub
If you can't use Decimal for your calculations, then you must write your code to account for the fact that binary floating point types can't represent some fractional values exactly. So rather than checking if the numbers are equal, you check if they are nearly equals.
You can use code like the following (taken from this article by Michael Borgwardt:
This is the VB translation, but not tested extensively.
Public Shared Function NearlyEqual(a As Double, b As Double, epsilon As Double) As Boolean
Dim absA As Double = Math.Abs(a)
Dim absB As Double = Math.Abs(b)
Dim diff As Double = Math.Abs(a - b)
If (a = b) Then
'shortcut, handles infinities
Return True
Else
If (a = 0 OrElse b = 0 OrElse diff < Double.Epsilon)
'a or b is zero or both are extremely close to it
'relative error is less meaningful here
Return diff < epsilon
Else
'use relative error
Return diff / (absA + absB) < epsilon
End If
End If
End Function

vba average calculator userform

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.

"Conversion from string " " to type 'Double' is not valid"

I am doing a project in microsoft visual studio 2012 and i am trying to write an application to determine the module average.
The script is the following:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles confirm.Click
Dim sum As Double
sum = CDbl(test.Text) * 50% + CDbl(project.Text) * 30% + CDbl(quiz.Text) * 30%
Dim modulemark As Double
modulemark = CDbl(CAmarks.Text) * 50% + CDbl(exam.Text) * 50%
Dim Grade As String
If sum < 40 Then
Grade = "F"
ElseIf sum >= 40 And modulemark < 65 And modulemark >= 40 Then
Grade = "C"
ElseIf sum >= 40 And modulemark < 75 And modulemark >= 65 Then
Grade = "B"
Else
Grade = "A"
End If
The script is intended to calculate the marks and give a grade after clicking a button named "Confirm".
However, when i tried to run the coding it said:
An unhandled exception of type 'System.InvalidCastException' occurred in Microsoft.VisualBasic.dll
Additional information: Conversion from string "" to type 'Double' is not valid.
can someone see what is wrong? i am new to Visual studio and i appreciate your help.
P.S. edited recent script.
P.S. Thank you for the user "Tim" for the script but for some unknown reason on the line "Double.TryParse(caMarks.Text, caMarks)" there's a blue squiggly that directs to caMarks that says "'Text' is not a member of 'double'". This is literally pulling my hair off! please help!
It looks like the names of the objects conflicted.
As Idle_Mind said in their answer, Double.TryParse is the way to go. This provides a safe way to attempt to convert a value to a double. If the conversion succeeds, the method returns true, and the resulting double is returned in the out parameter. If the conversion fails, false is returned and the default value of double (which is 0) is returned.
A simple example:
Dim result As Double
Dim score As String = "75"
If Double.TryParse(score, result) Then
' result will be a double with the value of 75
Else
' The conversion attempt failed, and result will have a value of 0
End If
So to apply that to your method (with no validation, though Idle_Mind's answer gives a good approach):
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles confirm.Click
Dim sum As Double
Dim modulemark As Double
Dim testScore As Double
Dim projectScore As Double
Dim quizScore As Double
Dim marks As Double
Dim examScore As Double
Double.TryParse(test.Text, testScore)
Double.TryParse(project.Text, projectScore)
Double.TryParse(quiz.Text, quizScore)
Double.TryParse(CAmarks.Text, marks)
Double.TryParse(exam.Text, examScore)
sum = (testScore * .5) + (projectScore * .3) + (quizScore * .3)
modulemark = (marks * .5) + (examScore * .5)
Dim Grade As String
If sum < 40 Then
Grade = "F"
ElseIf sum >= 40 And modulemark < 65 And modulemark >= 40 Then
Grade = "C"
ElseIf sum >= 40 And modulemark < 75 And modulemark >= 65 Then
Grade = "B"
Else
Grade = "A"
End If
End Sub
Explanation of the above code.
First, 6 Double variables are declared - this necessary because Double.TryParse takes an out parameter as the second argument, and that must be declared before its used. You could use one variable (and reuse it), but for simplicity I chose one for each score.
Once the scores have been parsed (successfully or not) the cumulative, weighted totals are determined. Note that parentheses were used when applying the weight modifier, to ensure operator precedence doesn't give you a result other than expected.
Hopefully this clarifies things for you.
Since you're assigning a letter grade to this variable...
Change Dim Grade As Integer
to Dim Grade As String.
Example of Double.TryParse():
Dim testValue As Double, projectValue As Double, quizValue As Double
If Double.TryParse(test.Text, testValue) Then
If Double.TryParse(project.Text, projectValue) Then
If Double.TryParse(quiz.Text, quizValue) Then
Dim sum As Double
sum = testValue * 0.5 + projectValue * 0.3 + quizValue * 0.3
' ... do something with "sum" ...
Else
MessageBox.Show(quiz.Text, "Invalid Quiz Score")
End If
Else
MessageBox.Show(project.Text, "Invalid Project Score")
End If
Else
MessageBox.Show(test.Text, "Invalid Test Score")
End If