Wrong result if variables are not declared to 0 in calculator - vb.net

everyone. I'm trying to make a basic calculator and as of now I only have the addition, sqrt, and % working. What confuses me is that, if I don't declare the first number to 0 after performing an operation, it gives me the wrong result.
For example:
num2 += Val(displayPanel.Text)
result = num1 + num2
displayPanel.Text = result
num1 = 0
num2 = 0
This certain code will give me correct results while
num2 += Val(displayPanel.Text)
result = num1 + num2
displayPanel.Text = result
will give me the answer '5' if I put 1+1 = 2 + 1. Same goes for my other classes where:
num1 += Val(displayPanel.Text)
result = Math.Sqrt(num1)
displayPanel.Text = result
num1 = 0
gives me the right result after doing sqrt of 9 + 1 which returns 4. Whereas if I removed the num1 = 0, doing sqrt of 9 + 1 will be 13. It seems that it's ignoring the operand and instead joins the two numbers as one string for the sqrt method.
My program is working fine now but can someone explain to me how not declaring num1 or num2 to 0 gives me an incorrect calculation?
Public Class Form1
Dim num1 As Single
Dim num2 As Single
Dim result As Single
addBtn_Click(sender As Object, e As EventArgs) Handles addBtn.Click
num1 += Val(displayPanel.Text)
displayPanel.Text = ""
equalBtn_Click(sender As Object, e As EventArgs) Handles equalBtn.Click
num2 += Val(displayPanel.Text)
result = num1 + num2
displayPanel.Text = result
num1 = 0
num2 = 0
sqrt_Click(sender As Object, e As EventArgs) Handles sqrt.Click
num1 += Val(displayPanel.Text)
result = Math.Sqrt(num1)
displayPanel.Text = result
num1 = 0

I think you might have problem with +=. Maybe change it to a simple =.
It's a bit hard to know everything that is going on here since I don't see all the code. Assuming num1 is equal to 0 at the beginning of the problem.
Also, let's convert += to something equivalent but easier to understand.
num1 = num1 + Val(displayPanel.Text)
result = Math.Sqrt(num1)
displayPanel.Text = result
If I write 9 in the textbox, num1 would be equal to 9. But if I execute it a second time, num1 would be equal to 18 since it's adding itself to the current value.

Your question "explain to me how not declaring num1 or num2 to 0 gives me an incorrect calculation?"
You have declared num1 and num2 at the class level (Form level) They will hold there assigned values until the entire form goes out of scope. You need them to hold there value so you can refer to these values in more than one method.
When you use the += you are asking the compiler to take the existing value and add a new value to it. with a simple assignment = you overwrite the value contained in the varaible.
Since += adds to the existing value instead of overwriting it completely you need to reset it to zero after each calculation.
If you used = you would not need to reset to zero because it would be overwritten.

The Val function in Vb.net is not operating as you are expecting it to.
Val attempts to convert textual information to a double or an integer.
See the documentation for it here.
Note that it explicitly states this:
(The Val function) stops converting at the first character that cannot be interpreted as a numeric digit, numeric modifier, numeric punctuation, or white space.
That is why it completely skips the = operator, and should skip parts like sqrt of; they are not a numeric modifiers, punctuation, digits, or whitespace.
Also of note, you may be seeing incorrect values because of multiple test cases in succession. Since your num1 and num2 variables have values that are not cleared, each additional evaluation will reuse the variables as they are.

Related

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

Convert String to integer and get null equal to 0

May I know got any simple way to perform this? It will hit an error when a.text is null. If I don't detect one by one, With a simple code may I convert a.text null to 0?
Dim count1 As Integer = 0
count1 = Convert.ToInt32(a.Text) + Convert.ToInt32(b.Text) + Convert.ToInt32(c.Text)
txt_display.Text = count1
Got any other method rather that I do like below detect one by one.
if a.Text = "" Then
a.Text = 0
End If
You have to detect one by one. Better way, will be to create your own function. Try below.
Dim count1 As Integer = 0
count1 = ConvertToInteger(a.Text) + ConvertToInteger(b.Text) + ConvertToInteger(c.Text)
txt_display.Text = count1
Private Function ConvertToInteger(ByRef value As String) As Integer
If String.IsNullOrEmpty(value) Then
value = "0"
End If
Return Convert.ToInt32(value)
End Function
If your objective is to sum the values in your textboxes and ignore the textboxes that cannot be converted to integers then you can simply use Int32.TryParse.
It will set your variable to 0 if the text cannot be converted to an integer without throwing exceptions.
' In place of your textboxes
Dim x1 As String = "2"
Dim x2 As String = Nothing
Dim x3 As String = "5"
Dim a, b, c As Integer
Int32.TryParse(x1, a)
Int32.TryParse(x2, b)
Int32.TryParse(x3, c)
Dim result = a + b + c
Console.WriteLine(result)
Instead, if you want to write the "0" string into the textbox text to signal your user of the wrong input, then you have to check the textboxes one by one, again using Int32.TryParse
Dim value1 as Integer
if Not Int32.TryParse(a.Text, value1) Then
a.Text = "0"
End If
' Here the variable value1 contains the converted value or zero.
' repeat for the other textboxes involved
A different approach using If Operator:
Dim count1 As Integer = 0
count1 = If(String.IsNullOrEmpty(a.Text), 0, Convert.ToInt32(a.Text)) + If(String.IsNullOrEmpty(b.Text), 0, Convert.ToInt32(b.Text)) + If(String.IsNullOrEmpty(c.Text), 0, Convert.ToInt32(c.Text))
txt_display.Text = count1
Example:
If String.IsNullOrEmpty(a.Text) Then
a.Text = "0"
End If
As per the Microsoft Documentation, a null string (Nothing) is different from an empty string (""):
The default value of String is Nothing (a null reference). Note that this is not the same as the empty string (value "").
You also use the = operator, which can be tricky with String types. For more info, see this post and the answer which was awarded a 50 points bounty.
If you use If with only two arguments, the following code
If a.Text Is Nothing Then
a.Text = 0
End If
Can be turned into a one-liner: Dim MyNumber as Integer = If(a.Text, 0)
If you meant to work with empty strings, then you can use: Dim MyNumber as Integer = If(a.Text.Length = 0, 0, a.Text).
If you want to deal with both, you can use String.IsNullOrEmpty(a.Text) as suggested by the currently accepted answer; or you can use a.Text="", a.Text = String.Empty, or a.Text = vbNullString, which are all equal (see the post I referred to earlier)
Finally, note that the conversion from a String type to an Integer type is made implicitly. There is no need to use the explicit cast conversions such as CType() or Convert.ToInt32.

How do I reverse the Mod operator

I have a simple function that handles overflows with modulus operator
Private Function RandomizeBlock(seed As Integer, ByVal block() As Byte) As Byte()
Dim Generator As System.Random = New System.Random(seed)
Dim newblock(255) As Byte
Dim i As Integer = 0
For i = 0 To block.Length - 1
newblock(i) = (block(i) + Generator.Next(0, 256)) Mod 256
Next
Return newblock
End Function
How do I undo the randomization done to a block?
I know mod works like this:
253,254,255,0,1,2,3,4 overwraps on 0.
Do can I find the inverse of reverse here?
rndValue = Generator.Next(0, 256)
reverse_1 = ((256 - rndValue) + block(i)) Mod 256
reverse_2 = ((256 + rndValue) - block(i)) Mod 256
If you know the random value, then reconstructing the original value is very simple.
You just have to keep in mind that working modulo p, you don't have actual numbers but remainder classes. The first p natural numbers are usually used as the representatives of these classes. Luckily, subtraction and addition are perfectly compatible with remainder classes.
The Mod implementation of VB converts any positive number to the representative of its remainder class. However, it can not do it on negative numbers. You have to do it on your own.
Long story short, this is the code:
Dim reverse As Integer = block(i) - rndValue;
If reverse < 0 Then reverse = reverse + 256 'Convert to representative of remainder class

How does this code I found work? Cipher [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
So I found this cool cipher, im making a project for school (its kinda like, show us everything you learned kinda of project) and were allowed to get heavy look at other peoples code online (as long is its not a complete copy and paste). I want to understand how it works and make my own version (without copy and pasting), ive put comments for the parts I understand, and question marks for the parts I dont.
Function EncryptDecrypt(ByVal text1 As String, ByVal key As String, ByVal isEncrypt As Boolean) As String //yea got this
Dim char1 As String //Defining char one
Dim char2 As String //Defining char two
Dim cKey As Byte //Defining a key as a byte
Dim strLength As Integer //Defining strLength as an integer
Dim Result As String = "" //Defining Result as String equal to nothing
Dim j As Integer = -1 //Defining j as an integer equal to -1
If text1 <> "" And IsNumeric(key) Then //if text1 is not nothing and the key is numeric then...
strLength = text1.Length //making strLength equal to the length of the text.
For i As Integer = 0 To strLength - 1 //Do until strLenth is less than 1 ???
char1 = text1.Substring(i, 1) //Char one is equal to
If j < key.Length - 1 Then //if j (-1) is less than the key's length - 1 then...
j = j + 1 //add one to j
Else //no explanation needed
j = 0 //no explanation needed
End If //no explanation needed
cKey = Val(key.Substring(j, 1)) //?? cKey is equal to value of the current character it is looking at (j, 1)??
If isEncrypt Then //if were encypting it
If (Asc(char1) + cKey) > 255 Then //????
char2 = Chr(Asc(char1) + cKey - 255) //????
Else //no explanation needed
char2 = Chr(Asc(char1) + cKey) //??
End If //no explanation needed
Else //no explanation needed
If (Asc(char1) - cKey) < 1 Then //????
char2 = Chr(Asc(char1) - cKey + 255) //?????
Else //no explanation needed
char2 = Chr(Asc(char1) - cKey) //?????
End If //no explanation needed
End If //no explanation needed
Result &= char2 //?????
Next //no explanation needed
Else //no explanation needed
MsgBox("Enter text or key!") //no explanation needed
End If //no explanation needed
Return Result //no explanation needed
End Function //no explanation needed
No explanation needed for these either VVVV
Private Sub btCrypt_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btCrypt.Click
txtResult.Text = EncryptDecrypt(txtText.Text, txtKey.Text, True)
End Sub
Private Sub btDecrypt_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btDecrypt.Click
txtResult.Text = EncryptDecrypt(txtText.Text, txtKey.Text, False)
End Sub
End Class
I kinda understand that each "letter" has an "Asc" value, but im not entirely sure how that works. Can anyone help, would be really great and helpful for me!
A good source of information can be found on the MSDN website. Below are some of the places you where getting stuck on. I've included references to the relivant documenation so if you still don't understand you can go away and see
For i As Integer = 0 To strLength - 1
Do until strLenth is equal to 0 decrementing 1 at a time
for loops msdn
cKey = Val(key.Substring(j, 1)) //?? cKey is equal to value of the
current character it is looking at (j, 1)??
Yes, cKey will be assigned the last char of the key. If they key was 854824 then cKey is now '4'.
If (Asc(char1) + cKey) > 255 Then //????
char2 = Chr(Asc(char1) + cKey - 255) //????
Else //no explanation needed
char2 = Chr(Asc(char1) + cKey) //??
End If //no explanation needed
Here this formula is used for decrying. What is useful to know here is that the Asc function will return the Asci Value for the character (table here)
A Asci character of a Single-byte Character Sets can not be greater than 255 or less than 0, so if it is greater, then they -255 + the cKey to find the original Ascii value.
If (Asc(char1) - cKey) < 1 Then //????
char2 = Chr(Asc(char1) - cKey + 255) //?????
Else //no explanation needed
char2 = Chr(Asc(char1) - cKey) //?????
End If //no explanation needed
Same as above but in reverse, this code will encrypt the character value by incrementing 255 to the char value if the ASCII value of char1 - x (where x = the numeric value of the last digit in the cKey) is greater than 0.
Result &= char2 //?????
and lastly this will concatanate the string onto Result each loop.
This is short hand for writing
Result = Result + Char2
You'll probably understand it more stepping through the program in debug and seeing the values of the variables as you go along.
Best of luck to you,
hope this helps
It cycles through the input character-by-character, and the key (all numbers, one digit at a time). Each character is converted to its ASCII character code, and has the key digit added or subtracted from it.
When it runs out of key, it loops back to the start of the key and goes through again.
If adding/subtracting gets a character outside the ASCII character code table (<0 or >255), it wraps round to the other side of the table.
And builds up a string of output, character by character.

Mod with Doubles

Am I doing something wrong or does the VBA Mod operator actually not work with floating point values like Doubles?
So I've always sort of assumed that the VBA Mod operator would work with Doubles based on the VB documentation, but in trying to figure out why my rounding function doesn't work, I found some unexpected Mod behavior.
Here is my code:
Public Function RoundUp(num As Double, Optional nearest As Double = 1)
RoundUp = ((num \ nearest) - ((num Mod nearest) > 0)) * nearest
End Function
RoundUp(12.34) returns 12 instead of 13 so I dug a little deeper and found that:
12.5 Mod 1 returns 0 with the return type of Long, whereas I had expected 0.5 with a type of Double.
Conclusion
As #ckuhn203 points out in his answer, according to the VBA specification,
The modulus, or remainder, operator divides number1 by number2
(rounding floating-point numbers to integers) and returns only the
remainder as result.
And
Usually, the data type of result is a Byte, Byte variant, Integer,
Integer variant, Long, or Variant containing a Long, regardless of
whether or not result is a whole number. Any fractional portion is
truncated.
For my purposes, I need a floating point modulo and so I have decided to use the following:
Public Function FMod(a As Double, b As Double) As Double
FMod = a - Fix(a / b) * b
'http://en.wikipedia.org/wiki/Machine_epsilon
'Unfortunately, this function can only be accurate when `a / b` is outside [-2.22E-16,+2.22E-16]
'Without this correction, FMod(.66, .06) = 5.55111512312578E-17 when it should be 0
If FMod >= -2 ^ -52 And FMod <= 2 ^ -52 Then '+/- 2.22E-16
FMod = 0
End If
End Function
Here are some examples:
FMod(12.5, 1) = 0.5
FMod(5.3, 2) = 1.3
FMod(18.5, 4.2) = 1.7
Using this in my rounding function solves my particular issue.
According to the VB6/VBA documentation
The modulus, or remainder, operator divides number1 by number2
(rounding floating-point numbers to integers) and returns only the
remainder as result. For example, in the following expression, A
(result) equals 5. A = 19 Mod 6.7 Usually, the data type of result is
a Byte, Byte variant, Integer, Integer variant, Long, or Variant
containing a Long, regardless of whether or not result is a whole
number. Any fractional portion is truncated. However, if any
expression is Null, result is Null. Any expression that is Empty is
treated as 0.
Remember, mod returns the remainder of the division. Any integer mod 1 = 0.
debug.print 12 mod 1
'12/1 = 12 r 0
The real culprit here though is that vba truncates (rounds down) the double to an integer before performing the modulo.
?13 mod 10
'==>3
?12.5 mod 10
'==>2
debug.print 12.5 mod 1
'vba truncates 12.5 to 12
debug.print 12 mod 1
'==> 0
I believe that the Mod operator calculates with long type only. The link that you provided is for VB.Net, which is not the same as the VBA you use in MSAccess.
The operator in VBA appears to accept a double type, but simply converts it to a long internally.
This test yielded a result of 1.
9 Mod 4.5
This test yielded a result of 0.
8 Mod 4.5
As a work around your can do some simple math on the values. To get two decimal of precision just multiply the input values by 100 and then divide the result by 100.
result = (123.45*100 Mod 1*100)/100
result = (12345 Mod 100)/100
result = 0.45
I'm late to the party, but just incase this answer is still helpful to someone.
Try This in VBS:
Option Explicit
Call Main()
Sub Main()
WScript.Echo CStr(Is_Rest_Of_Divide_Equal_To_Zero(506.25, 1.5625))
End Sub
Function Is_Rest_Of_Divide_Equal_To_Zero(Divident, Divisor)
Dim Result
Dim DivideResult
If Divident > Divisor Then
DivideResult = Round(Divident/Divisor, 0)
If (DivideResult * Divisor) > Divident Then
Result = False
ElseIf (DivideResult * Divisor) = Divident Then
Result = True
ElseIf (DivideResult * Divisor) < Divident Then
Result = False
End If
ElseIf Divident = Divisor Then
Result = True
ElseIf Divident < Divisor Then
Result = False
End If
Is_Rest_Of_Divide_Equal_To_Zero = Result
End Function
Public Function Modi(d as double) as double
Modi = d - Int(d)
End Function
Dim myDoule as Double
myDoule = 1.99999
Debug.Print Modi(myDoule)
0.99999