VB - Negative Number and Conditional Statement - vb.net

Back again with another noob question that is bugging the crap out of me. I spent 2 days trying figure this one out. It seems that me and VB and negative numbers always have a misunderstanding.
I got everything else in the program for a fat percentage calculator to work except the conditional statement that SHOULD print out an error message if either the double conversion of two textbox strings is less than zero. However, even though I enter negative numbers for both when testing, the program skips over my Else error statement and calculates the two negative numbers anyway and gets some wholly ridiculous fat percentage number. It doesn't even seem as if it's just going through with the expressions if the "If-Then" part of the code as I did the math and the percentage answer does not match up.
Private Sub btnCalculate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculate.Click
'Declare variables
Dim dblCaloriesInFood As Double
Dim dblGramsOfFat As Double
Dim dblPercentageOfCaloriesFromFat As Double
Dim dblCaloriesFromFat As Double
'Always initialize lblLowInFat message as not visible when button is clicked
lblLowInFat.Visible = False
Try
'Converting textbox strings to double.
dblCaloriesInFood = CDbl(txtCaloriesInFood.Text)
dblGramsOfFat = CDbl(txtGramsOfFat.Text)
If dblCaloriesInFood Or dblGramsOfFat > 0 Then
'Calculate Calories from fat
dblCaloriesFromFat = dblGramsOfFat * 9
'Calculate percentage of calories from fat
dblPercentageOfCaloriesFromFat = dblCaloriesFromFat / dblCaloriesInFood
'Display percentage of calories from fat
If dblPercentageOfCaloriesFromFat >= 0 Then
lblMessage.Text = dblPercentageOfCaloriesFromFat.ToString("p")
Else
lblMessage.Text = String.Empty
End If
'Display low fat message
If dblPercentageOfCaloriesFromFat <= 0.3 And dblPercentageOfCaloriesFromFat > 0 Then
lblLowInFat.Visible = True
End If
Else
'really tried to make this message work but couldn't figure it out.
'why does it only display this message when zero is entered and not when
'negative numbers are entered. instead it just calculates the negative numbers
'as if they were whole positive numbers or something, not sure because the percentage
'is way off the charts when i enter negative numbers. can't figure this out.
MessageBox.Show("Either the calories or fat grams were incorrectly entered")
txtCaloriesInFood.Text = String.Empty
txtGramsOfFat.Text = STring.Empty
txtCaloriesInFood.Focus()
End If
Catch
'error message for invalid input
MessageBox.Show("Please enter a numeric value for calories in food & number of fat grams.")
txtCaloriesInFood.Focus()
End Try
End Sub

Your If statement is wrong. First, you need to test the two values separately. Second, you want both values to be greater than zero, so you should use And instead of Or (which means only one has to be greater than zero).
If dblCaloriesInFood > 0 And dblGramsOfFat > 0 Then

Related

Check if textbox contains specific decimal numbers

I'm doing a school project but we weren't really taught about complex parts like this, so I'm hoping someone can help me.
I want my program to check whether my textbox contains 0.1-0.4 decimal numbers when I click a button. If yes, a message box will show that 0.5 or any half number must be entered. But, if the textbox contains 0.6-0.9, the message box will say to enter a whole number.
I know the message box part, I just don't know how to check those specific decimal numbers in the textbox. And also, if the textbox contain a whole number or a half number (.5), it will proceed to do what is stated.
If (Textbox1=0.1-0.4) Then
MsgBox("Enter a half number.")
ElseIf (TextBox1=0.6-0.9) Then
MsgBox("Enter a whole number.")
ElseIf (TextBox1=Half Number or TextBox1=Whole Number) Then
MsgBox("Transaction complete.")
End If
You can use a Select statement once you have converted the TextBox's Text to a numeric value:
Sub CheckDecimals(number As String)
If IsNumeric(number) Then
' Convert number to Single
Dim singleNumber As Single = CSng(number)
' Use Mod to get fraction only
Dim modNumber As Single = singleNumber Mod 1
Select Case modNumber
Case 0, 0.5
MsgBox("Transaction complete.")
Case Is < 0.5
MsgBox("Enter a half number.")
Case Is > 0.5
MsgBox("Enter a whole number.")
End Select
Else
MsgBox("Please enter a number.")
End If
End Sub
In your button Click event handler, use:
CheckDecimals(TextBox1.Text)

Variant and if statement - VBA [duplicate]

I have trouble comparing 2 double in Excel VBA
suppose that I have the following code
Dim a as double
Dim b as double
a = 0.15
b = 0.01
After a few manipulations on b, b is now equal to 0.6
however the imprecision related to the double data type gives me headache because
if a = b then
//this will never trigger
end if
Do you know how I can remove the trailing imprecision on the double type?
You can't compare floating point values for equality. See this article on "Comparing floating point numbers" for a discussion of how to handle the intrinsic error.
It isn't as simple as comparing to a constant error margin unless you know for sure what the absolute range of the floats is beforehand.
if you are going to do this....
Dim a as double
Dim b as double
a = 0.15
b = 0.01
you need to add the round function in your IF statement like this...
If Round(a,2) = Round(b,2) Then
//code inside block will now trigger.
End If
See also here for additional Microsoft reference.
It is never wise to compare doubles on equality.
Some decimal values map to several floating point representations. So one 0.6 is not always equal to the other 0.6.
If we subtract one from the other, we probably get something like 0.00000000051.
We can now define equality as having a difference smaller that a certain error margin.
Here is a simple function I wrote:
Function dblCheckTheSame(number1 As Double, number2 As Double, Optional Digits As Integer = 12) As Boolean
If (number1 - number2) ^ 2 < (10 ^ -Digits) ^ 2 Then
dblCheckTheSame = True
Else
dblCheckTheSame = False
End If
End Function
Call it with:
MsgBox dblCheckTheSame(1.2345, 1.23456789)
MsgBox dblCheckTheSame(1.2345, 1.23456789, 4)
MsgBox dblCheckTheSame(1.2345678900001, 1.2345678900002)
MsgBox dblCheckTheSame(1.2345678900001, 1.2345678900002, 14)
As has been pointed out, many decimal numbers cannot be represented precisely as traditional floating-point types. Depending on the nature of your problem space, you may be better off using the Decimal VBA type which can represent decimal numbers (base 10) with perfect precision up to a certain decimal point. This is often done for representing money for example where 2-digit decimal precision is often desired.
Dim a as Decimal
Dim b as Decimal
a = 0.15
b = 0.01
Late answer but I'm surprised a solution hasn't been posted that addresses the concerns outlined in the article linked in the (currently) accepted answer, namely that:
Rounding checks equality with absolute tolerance (e.g. 0.0001 units if rounded to 4d.p.) which is rubbish when comparing different values on multiple orders of magnitude (so not just comparing to 0)
Relative tolerance that scales with one of the numbers being compared meanwhile is not mentioned in the current answers, but performs well on non-zero comparisons (however will be bad at comparing to zero as the scaling blows up around then).
To solve this, I've taken inspiration from Python: PEP 485 -- A Function for testing approximate equality to implement the following (in a standard module):
Code
'#NoIndent: Don't want to lose our description annotations
'#Folder("Tests.Utils")
Option Explicit
Option Private Module
'Based on Python's math.isclose https://github.com/python/cpython/blob/17f94e28882e1e2b331ace93f42e8615383dee59/Modules/mathmodule.c#L2962-L3003
'math.isclose -> boolean
' a: double
' b: double
' relTol: double = 1e-09
' maximum difference for being considered "close", relative to the
' magnitude of the input values
' absTol: double = 0.0
' maximum difference for being considered "close", regardless of the
' magnitude of the input values
'Determine whether two floating point numbers are close in value.
'Return True if a is close in value to b, and False otherwise.
'For the values to be considered close, the difference between them
'must be smaller than at least one of the tolerances.
'-inf, inf and NaN behave similarly to the IEEE 754 Standard. That
'is, NaN is not close to anything, even itself. inf and -inf are
'only close to themselves.
'#Description("Determine whether two floating point numbers are close in value, accounting for special values in IEEE 754")
Public Function IsClose(ByVal a As Double, ByVal b As Double, _
Optional ByVal relTol As Double = 0.000000001, _
Optional ByVal absTol As Double = 0 _
) As Boolean
If relTol < 0# Or absTol < 0# Then
Err.Raise 5, Description:="tolerances must be non-negative"
ElseIf a = b Then
'Short circuit exact equality -- needed to catch two infinities of
' the same sign. And perhaps speeds things up a bit sometimes.
IsClose = True
ElseIf IsInfinity(a) Or IsInfinity(b) Then
'This catches the case of two infinities of opposite sign, or
' one infinity and one finite number. Two infinities of opposite
' sign would otherwise have an infinite relative tolerance.
'Two infinities of the same sign are caught by the equality check
' above.
IsClose = False
Else
'Now do the regular computation on finite arguments. Here an
' infinite tolerance will always result in the function returning True,
' since an infinite difference will be <= to the infinite tolerance.
'This is to supress overflow errors as we deal with infinity.
'NaN has already been filtered out in the equality checks earlier.
On Error Resume Next
Dim diff As Double: diff = Abs(b - a)
If diff <= absTol Then
IsClose = True
ElseIf diff <= CDbl(Abs(relTol * b)) Then
IsClose = True
ElseIf diff <= CDbl(Abs(relTol * a)) Then
IsClose = True
End If
On Error GoTo 0
End If
End Function
'#Description "Checks if Number is IEEE754 +/- inf, won't raise an error"
Public IsInfinity(ByVal Number As Double) As Boolean
On Error Resume Next 'in case of NaN
IsInfinity = Abs(Number) = PosInf
On Error GoTo 0
End Function
'#Description "IEEE754 -inf"
Public Property Get NegInf() As Double
On Error Resume Next
NegInf = -1 / 0
On Error GoTo 0
End Property
'#Description "IEEE754 +inf"
Public Property Get PosInf() As Double
On Error Resume Next
PosInf = 1 / 0
On Error GoTo 0
End Property
'#Description "IEEE754 signaling NaN (sNaN)"
Public Property Get NaN() As Double
On Error Resume Next
NaN = 0 / 0
On Error GoTo 0
End Property
'#Description "IEEE754 quiet NaN (qNaN)"
Public Property Get QNaN() As Double
QNaN = -NaN
End Property
Updated to incorporate great feedback from Cristian Buse
Examples
The IsClose function can be used to check for absolute difference:
assert(IsClose(0, 0.0001233, absTol:= 0.001)) 'same to 3 d.p.?
... or relative difference:
assert(IsClose(1234.5, 1234.6, relTol:= 0.0001)) '0.01% relative difference?
... but generally you specify both and if either tolerance is met then the numbers are considered close. It has special handling of +-infinity which are only close to themselves, and NaN which is close to nothing (see the PEP for full justification, or my Code Review post where I'd love feedback on this code :)
The Currency data type may be a good alternative. It handles relatively large numbers with fixed four digit precision.
Work-a-round??
Not sure if this will answer all scenarios, but I ran into a problem comparing rounded double values in VBA. When I compared to numbers that appeared to be identical after rounding, VBA would trigger false in an if-then compare statement.
My fix was to run two conversions, first double to string, then string to double, and then do the compare.
Simulated Example
I did not record the exact numbers that caused the error mentioned in this post, and the amounts in my example do not trigger the problem currently and are intended to represent the type of issue.
Sub Test_Rounded_Numbers()
Dim Num1 As Double
Dim Num2 As Double
Let Num1 = 123.123456789
Let Num2 = 123.123467891
Let Num1 = Round(Num1, 4) '123.1235
Let Num2 = Round(Num2, 4) '123.1235
If Num1 = Num2 Then
MsgBox "Correct Match, " & Num1 & " does equal " & Num2
Else
MsgBox "Inccorrect Match, " & Num1 & " does not equal " & Num2
End If
'Here it would say that "Inccorrect Match, 123.1235 does not equal 123.1235."
End Sub
Sub Fixed_Double_Value_Type_Compare_Issue()
Dim Num1 As Double
Dim Num2 As Double
Let Num1 = 123.123456789
Let Num2 = 123.123467891
Let Num1 = Round(Num1, 4) '123.1235
Let Num2 = Round(Num2, 4) '123.1235
'Add CDbl(CStr(Double_Value))
'By doing this step the numbers
'would trigger if they matched
'100% of the time
If CDbl(CStr(Num1)) = CDbl(CStr(Num2)) Then
MsgBox "Correct Match"
Else
MsgBox "Inccorrect Match"
End If
'Now it says Here it would say that "Correct Match, 123.1235 does equal 123.1235."
End Sub
Depending on your situation and your data, and if you're happy with the level of precision shown by default, you can try comparing the string conversions of the numbers as a very simple coding solution:
if cstr(a) = cstr(b)
This will include as much precision as would be displayed by default, which is generally sufficient to consider the numbers equal.
This would be inefficient for very large data sets, but for me was useful when reconciling imported data which was identical but was not matching after storing the data in VBA Arrays.
Try to use Single values if possible.
Conversion to Double values generates random errors.
Public Sub Test()
Dim D01 As Double
Dim D02 As Double
Dim S01 As Single
Dim S02 As Single
S01 = 45.678 / 12
S02 = 45.678
D01 = S01
D02 = S02
Debug.Print S01 * 12
Debug.Print S02
Debug.Print D01 * 12
Debug.Print D02
End Sub
45,678
45,678
45,67799949646
45,6780014038086

Visual Basic - How to sort highest and lowest number of a series?

****I don't necessarily want the code to fix this problem, I would just like if someone would be able to explain to me why this is happening or what I could have done better to solve this problem.****
I have a homework assignment where I need to print out the highest and lowest number of a series.I am currently able to print out the highest and lowest numbers but if all of the numbers entered are positive, then it displays my lowest number as 0, and same if all the entered numbers are negative.
So if someone would be able to explain how to solve this, without necessarily giving away the answer, it would be greatly appreciated!
Here is my code:
Module Module1
'This is going to have the user enter a series of numbers,
'Once the user is finished have them enter '-99' to end the series,
'then it is going to return largest and smallest number
Sub Main()
NumSeries()
End Sub
'This is going to get the series from the users
Sub NumSeries()
Dim largeNum As Integer = 0
Dim smallNum As Integer = 0
Dim userNum As Integer = 0
Dim largeTemp As Integer = 0
Dim smallTemp As Integer = 0
Console.WriteLine("Please enter a series of positive and negative numbers")
Console.WriteLine("Then type '-99' to end the series")
Console.WriteLine()
While (userNum <> -99)
Console.Write("Enter num: ")
userNum = Console.ReadLine()
If (userNum > largeTemp) Then
largeTemp = userNum
largeNum = largeTemp
ElseIf (userNum < smallTemp And userNum <> -99) Then
smallTemp = userNum
smallNum = smallTemp
End If
End While
Console.WriteLine("The largest number is " & largeNum)
Console.WriteLine("The smallest number is " & smallNum)
End Sub
End Module
Two points:
You don't need the variables largeTemp and smallTemp.
(The answer to your question) You should initialize largeNum to a very small number, and smallNum to a very large number. For example, if smallNum is set to zero at the beginning of the program, only numbers smaller than zero will replace it. To find an error like this, you should trace through the program either by hand or with a debugger and see what happens at each step. It would be a good idea to do this now so you'll understand the problem. (Alternatively, as Idle-Mind pointed out, you can initialize largeNum and smallNum to the first item in the list.)
Don't use any sentinel values at all. Simply declare a Boolean variable so you know if you the value retrieved is the very first one or not:
Dim FirstEntry As Boolean = True
If it is, then set both largeNum and smallNum to that value. Now just compare each additional entered value with those stored values to see if they are bigger or smaller than the previously known extreme.
A few things you should improve upon:
You can remove the temp variables. They don't serve any other purpose other than unnecessarily consuming memory and CPU time.
You should initialize your min and max variables with highest and lowest possible values respectively. If you think larger than integer values should be allowed, change the type to Long
Dim largeNum As Integer = Integer.MinValue
Dim smallNum As Integer = Integer.MaxValue
Remove the ElseIf. Use two If statements instead. This is so that both variables will be set with the very first input itself.
If (userNum > largeNum) Then largeNum = userNum
If (userNum < smallNum) Then smallNum = userNum

VB.NET: use "Or" with a "equal or greater than" in a "If" statement

I cannot get me If statement to work properly.
I want my If statement to check my two textboxes: Name and Price, so that they are not empty and also check the Price textbox so the input price/number is equal or greater to 1.00.
Right now, the program warns me even if I type in a number/price higher than 1.00, something it only should do when the number is less than 1.00.
I am using: Option Explicit On, Option Strict On.
Private Sub Btn_ResrvCancl_Click(sender As Object, e As EventArgs) Handles Btn_ResrvCancl.Click, listBox_ResDisplay.DoubleClick
If listBox_ResDisplay.SelectedIndex >= 0 Then
If radioBtn_Reserve.Checked Then
If txt_Name.Text Is "" Or txt_Price.Text Is "" Or CDbl(txt_Price.Text) > 1.0 Then
MessageBox.Show("Please enter both your name and price (price must be 1.00 or higher)")
Else
Dim BookingSuccess As Boolean = seatmgrResrv.NewReservation(txt_Name.Text, CDbl(txt_Price.Text), CInt(listBox_ResDisplay.SelectedItem.ToString.Substring(0, 15).Trim))
If BookingSuccess = False Then
MessageBox.Show("Already booked!")
End If
End If
Else
Dim CancelSuccess As Boolean = seatmgrResrv.CancelReservation(CInt(listBox_ResDisplay.SelectedItem.ToString.Substring(0, 15).Trim))
If CancelSuccess = False Then
MessageBox.Show("Already vacant!")
End If
End If
UppsateList()
Else
MessageBox.Show("Please choose a seat")
End If
End Sub
For what I understand, this must be the row that are incorrect but I cannot figure out a solution:
If txt_Name.Text Is "" Or txt_Price.Text Is "" Or CDbl(txt_Price.Text) > 1.0
Thanks in advance!
The Is operator is for referential equality, i.e. whether two references refer to the same object, while you want value equality, for which you use the = operator. The compiler might optimise such that either will work but that's not a reason not to do it properly.
Also, you can't really use CDbl because it will throw an exception if the TextBox is empty or contains some other non-numerical value. If you were to use OrElse instead of Or then that would handle the empty case but not any other.
Finally, you want to notify the user if the amount is LESS THAN 1, not if it's greater than.
All up, this is what your code should look like:
Dim price As Decimal
If txt_Name.Text = String.Empty OrElse
Not Decimal.TryParse(txt_Price.Text, price) OrElse
price < Decimal.One Then
This part:
CDbl(txt_Price.Text) >= 1.0 Then
will cause your validation message to show when the price is greater than or equal to 1.0. Really you should be checking if the value is strictly less than 1.0:
CDbl(txt_Price.Text) < 1.0

MPG calculator not giving correct answer

I am trying to create a MPG calculator in Visual Basic, but it only calculates in whole numbers not decimals. When I input 10 into the gallons section and 375 into the miles section, the calculator only calculates 37 not 37.5.
Public Class Form1
Private Sub btnCalculateMpg_Click(sender As Object, e As EventArgs) Handles btnCalculateMpg.Click
'Declare variables for the calculation.
Dim intMpg As Integer
lblMpgCalculated.Text = String.Empty
Try
'Calculate and display Miles per Gallon.
intMpg = CInt(txtMiles.Text) \
CInt(txtGallons.Text)
lblMpgCalculated.Text = intMpg.ToString("N")
Catch
'Error Message.
MessageBox.Show("All input must be valid numeric values.")
End Try
End Sub
intMpg should not be an integer if you don't want to receive a result that's an integer. You may want to use a Double, in which case the line where you declare intMpg would be:
Dim intMpg As Double
You also should use the / operator for division, not the \ operator, since the latter performs integral division, as explained here.
So, the line where you perform the division should be:
intMpg = CInt(txtMiles.Text) / CInt(txtGallons.Text)
If you perform these changes, intMpg would be a misleading name (since it's not an integer, even though the name makes it sound like it is), so you should change it to something else like milesPerGallon.
Integers are only whole numbers. Storing the result of a division in an Integer will do an integer division, which means your result will also be an integer and the decimal will get truncated. To get decimals, you should declare your result variable as a Double.