VB.Net Double comparison after some additions - vb.net

I've faced a weird case with a double variable after adding some values to it.
The problem occurs when adding (0.2) to a double variable more than one time -I think it only happens with (0.2)- for example: consider this code:
Dim i As Double = 2
i = i + 0.2
MsgBox(i) '2.2
MsgBox(i > 2.2) 'False >> No problem
But if I add (0.2) more than one time:
Dim i As Double = 2
i = i + 0.2
i = i + 0.2
MsgBox(i) '2.4
Msgbox(i > 2.4) 'True >> !!!!
Also
Dim i As Double = 2
For x As Integer = 1 to 5
i = i + 0.2
Next
MsgBox(i) '3
Msgbox(i > 3) 'True >> !!!!
I tried the same code with other values, I don't get this issue:
Dim i As Double = 2
i = i + 0.5
i = i + 0.5
MsgBox(i) '3
Msgbox(i > 3) 'False >> No problem
Anyone has an explanation for this??
Thank you

If you take the example 3 you would see that the result is actually 3.0000000000000009.
The problem is in the rounding of a double.
If you change the data type decimal the problem is fixed:
Sub Main()
Dim i As Decimal = 2
For x As Integer = 1 To 5
i = i + 0.2
Next
MsgBox(i) '3
MsgBox(i > 3) 'False >> No problem
End Sub
This is about C# but, I guess, it the same thing for vb.net.

This issue known as "Accuracy Problems (Wikipedia Link)"
The fact that floating-point numbers cannot precisely represent all
real numbers, and that floating-point operations can not precisely
represent true arithmetic operations, leads to many surprising
situations. This is related to the finite precision with which
computers generally represent numbers.

Related

Is there a way to avoid the error statement "Arithmetic operation resulted in an overflow."?

I've done some research and the general advice is changing the datatype of the variable holding the expression to Long or Ulong but in either case, I'm still getting the same error. I also tried enclosing the expression with CInt() (or CLong()) to force it to cut out it's decimal portion to reduce the length of the output of the expression but neither is working. It's all pretty confusing. Any help will be deeply appreciated. The code block triggering the error is as follows;
Vsf(i) = CInt((((0.91544 - 0.00166 * Angle(i) - 0.000002 * W - 0.054248 *
Superelevation(i) - Sidefrictionfactor) / 0.013939) * Radius(i)) ^ 0.5)
Vro(i) = CInt((((1.04136 - 0.004528 * Angle(i) - 0.000004 * W - 0.338711 * Superelevation(i) - rolloverthreshold) / 0.014578) * Radius(i)) ^ 0.5)
Vmin(i) = CInt(Math.Min(Vsf(i), Vro(i)))
I declared VSf(), Vro() and Vmin() all as integer arrays. I even enclosed the computation expression in a CInt() in hopes that it would convert the result of Vro (which was triggering the arithmetic overflow error) to an integer and hence not have to deal with decimals which would lead to more digits. Yet when I hover over Vro(i), I see a 4 digit integer with decimals. Not sure why that's happening.
I broke the formula down to try and see where the problem was occurring. Everything buzzed along until the very end. If the value to be raised to .5 is a negative number, the square root does not produce a number. I would perform the steps of the formula without the ^.5 and then check for a negative number.
Private Sub OPCode()
Dim Angle = 90.2
Dim Radius = 100.2
Dim Superelevation = 37.8
Dim Sidefrictionfactor = 0.003
Dim W = 0.00325
Dim Vsf As Double
Dim AngleMultiplication = 0.00166 * Angle
Dim WMultiplication = 0.000002 * W
Dim SuperelavationMultiplication = 0.054248 * Superelevation
Dim SubtractResult = 0.91544 - AngleMultiplication - WMultiplication - SuperelavationMultiplication - Sidefrictionfactor
Dim DivisionResult = SubtractResult / 0.013939
Dim MultiplyByRadius = DivisionResult * Radius
Debug.Print(MultiplyByRadius.ToString) 'With my made up values I get a negative number
Vsf = MultiplyByRadius ^ 0.5 'You cannot get the square root of a negative number
Debug.Print(Vsf.ToString) 'NaN
Dim intVsf = CInt(Vsf) 'The Arithematic overflow error occurs here
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

How to fit straight line to variable curve and determining the x-intercept

I'm trying to figure out how to code a straight line to the straight part of a curve, the curve should look something like the exponential, click the link to open the image:
Straight line to Curve and determining the x-intercept
Here is the code, I'm only using the exponential as an example
`
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim s As String
Dim xl As New Excel.Application
Dim wb As Excel.Workbook
Dim ws As Excel.Worksheet
wb = xl.Workbooks.Add 'create new workbook
ws = wb.Worksheets(1) 'select sheet 1
ws.Activate()
Dim Currents() As Double
Dim PhotodiodeValues() As Double
Dim AppliedCurrent As Double
'AppliedCurrent = SerialPort1.ReadLine
AppliedCurrent = 0
If AppliedCurrent >= 0 And AppliedCurrent < 0.1 Then
Dim j As Integer = 1
For i As Double = 0 To 5 Step 0.5
ReDim Preserve Currents(j)
ReDim Preserve PhotodiodeValues(j)
MsgBox(i)
MsgBox("LDI " & CType(i, String))
s = ("LDI " & CType(i, String))
AppliedCurrent = i
If AppliedCurrent >= i And AppliedCurrent < (i + 0.1) Then
Currents(j) = CType(i, Double)
Label1.Text = Currents(j)
PhotodiodeValues(j) = CType(Math.E ^ (i), Double)
ws.Cells(j, 1) = Currents(j)
ws.Cells(j, 2) = PhotodiodeValues(j)
Else
System.Threading.Thread.Sleep(1000)
End If
j = j + 1
Next
Else
System.Threading.Thread.Sleep(1000)
End If
sfd1.ShowDialog() 'get file name
wb.SaveAs(sfd1.FileName) 'save data to file
wb.Close()
xl = Nothing 'dispose of excel
ScatterGraph1.PlotXY(Currents, PhotodiodeValues)
'SerialPort1.Close()
End Sub
End Class`
First off, I'll explain my thought process. If I have misunderstood, please let me know and I will update my answer. The slope dy/dx of the curve y = e^x is dy/dx = e^x, a monotonically increasing function of x for all real x. There is never a point at which the function becomes linear and, while it has a horizontal asymptote (y = 0) it has no vertical asymptote.
I take it that what you want is the equation of a tangent line taken at a point where the slope first becomes greater than some cutoff value m*. After that point, the graph of y = e^x "might as well" be a straight line for your intents and purposes.
So, we must first solve the equation m* = dy/dx = e^x for the x at which m* occurs. The range of e^x is all positive real numbers and e^x is monotonically increasing, so any positive real number m* will have a unique solution x*. indeed, x* = ln(m*). Our tangent line will pass through the point (x*, e^x*) and have slope m*. Recall that m* = e^x*, so the point is (ln(m*), m*) and the slope is m*.
With a point and the slope, we can figure out the equation of a line. We have that the slope from the given point to any other point must be m*; so, (y - y*)/(x - x*) = m*. Rearranging, (y - y*) = m*(x - x*), y = mx - mx* + y*, and finally y = mx + (y - mx) = mx + (m - mln(m)). The Y-intercept is therefore (m* - mln(m)). We can get the X-intercept by setting y = 0 and solving for x: 0 = mx + (m - mln(m)), mx = mln(m*) - m*, x = ln(m*) - 1.
In summary:
the equation of the line tangent to y = e^x with slope m* is y = mx + (m - mln(m)).
the Y-intercept of this line is (m* - mln(m)).
the X-intercept of this line is ln(m*) - 1
If the curve is known at compile time, I recommend hard-coding a closed form analytical solution for the derivative and any asymptotes. If the function is not known until runtime, the derivative at a given point can be approximated numerically using a variety of methods. Intuitively, the definition of the derivative as the limit of (f(x+d) - f(x)) / d as d approaches zero can be used to find approximations of the derivative where the derivative (likely) exists. For well-behaved analytic functions, you will typically be safe except in special cases.
If the function's derivative is monotonically non-decreasing, as in this example, you can find the point (if any) at which the function's slope meets or exceeds a certain cutoff using approximation (as above) in conjunction with something akin to binary search. Start at a value such as x = 0, and increase or decrease x by some multiplicatively increasing factor until you have passed your target. Now, with bounds on the values of x between which your target can be found, check the middle of the range, and then either the left or right half recursively until a suitably good x* is found.

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

VBA Macro Run time error 6: overflow- coding inside a loop

Having a problem with this Error. I am creating a GA and the loop is to assign my fitness value to an array.
some of the variables
Dim Chromolength as integer
Chromolength = varchromolength * aVariables
Dim i as integer, j as integer, counter as integer
Dim Poparr() As Integer
Dim FitValarr() As Integer
the code:
ReDim Poparr(1 To PopSize, 1 To Chromolength)
For i = 1 To PopSize
For j = 1 To Chromolength
If Rnd < 0.5 Then
Poparr(i, j) = 0
Else
Poparr(i, j) = 1
End If
Next j
Next i
For i = 1 To PopSize
j = 1
counter = Chromolength
Do While counter > 0
FitValarr(i) = FitValarr(i) + Poparr(i, counter) * 2 ^ (j - 1)
j = j + 1
counter = counter - 1
Loop
Next i
I am having problems with:
FitValarr(i) = FitValarr(i) + Poparr(i, counter) * 2 ^ (j - 1)
I apologize, I am fairly new to VBA.
An overflow condition arises when you create an integer expression that evaluates to a value larger than can be expressed in a 16-bit signed integer. Given the expression, either the contents of FitValarr(i), or the expression 2^(j-1) could be overflowing. Suggest all the the variables presently declared as Int be changed to Long. Long integers are 32-bit signed values and provide a correspondingly larger range of possible values.
I had the same run time error 6. After much investigation l discovered that mine was a simple 'divide by zero' error.
I set up an integer value to hold Zip codes, and Error 6 events plagued me - until I realized that a zip code of 85338 exceeded the capacity of an int...
While I didn't think of a zip code as a "value" it was nonetheless certainly interpreted as one. I suspect the same could happen with addresses as well as other "non-numeric" numeric values. Changing the variable to a string resolved the problem.
It just didn't occur to me that a zip code was a "numeric value." Lesson learned.