I just want to know the reason for the run-time error: Overflow when running these VBA codes
Function target_time(Time) As Double
target_time = 1200 * 60 * Time
End Function
Sub Test()
x = target_time(24)
MsgBox x
End Sub
I already addressed this issue by rearranging the function formula into:
target_time = Time*1200 * 60
I just want to know why is repositioning the variable causes the runtime error overflow
Here's what VBA does:
1200 and 60 <-- Those are small enough to be a (16-bit) Integer, so I'll assume they are 16-bit Integer constants.
1200 * 60 <-- A multiplication of two 16-bit Integers yields an Integer - there is no automatic promotion to a 32-bit Long. Since 1200 * 60 = 72000 exceeds the maximum value of a 16-bit integer, the expression overflows.
On the other hand, Time * 1200 is not a multiplication of two 16-bit Integers, it's a multiplication of a "Variant containing an Integer" with an Integer. In this case, the result is automatically promoted to a "Variant containing a Long":
Dim v As Variant
v = 1200
Debug.Print TypeName(v) ' prints Integer
Debug.Print TypeName(v * 60) ' prints Long
Debug.Print TypeName(v * 6000000) ' prints Double(!)
The reason for this can be found in section 5.6.9.3 of the VBA spec:
If one or both operands have a declared type of Variant:
If the operator’s effective value type is Integer, Long, Single or Double, the operator’s
value type is the narrowest type of either Integer, Long or Double such that the
operator value is within the valid range of the type. If the result does not fit within
Double, runtime error 6 (Overflow) is raised.
As an additon to Heinzi's answer which addresses the OP's question I would like to add that I would add some of the following operators: CLng, CDbl to make sure that the result will be correct (or better closer to correct :-) )
Instead of target_time = 1200 * 60 * Time use target_time = CDbl(1200) * CDbl(60) * CDbl(Time).
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
I have created a sub that works out a quadratic equation after inputting 3 coefficients.
However, I have to round that answer to whatever the user wishes to between 1 decimal point to 5.
Module Module1
Public property MyAnswer As Object
Sub myQuadraticEquation()
... 'enter 3 coefficients
Dim d As Integer = b ^ 2 - 4 * a * c
Console.Write("Your roots are: ")
MyResult = (((-b + d) / (2 * a) & " , " & (-b - d) / (2 * a)))
Console.WriteLine(MyAnswer)
End Module
My question is, how can I round the answer from this mathematical feature to a certain amount of d.p based on users' liking?
Can I create another sub and then call it later using if statement inside the Quadratic equation?
It's giving me the following error
"System.FormatException: 'Input string was not in a correct format.'
I would suggest always declaring variables as the datatype you want instead of relying on the program to figure them out at runtime. So change Public property MyAnswer As Object to Public property MyAnswer As String. Because you know you expect MyAnswer to be a string at the end.
As far as the rounding is concerned, I believe you were attempting to do it too late. I would change
MyAnswer = (((-b + d) / (2 * a) & " , " & (-b - d) / (2 * a)))
to
Dim part1 As Decimal = Math.Round((-b + d) / (2 * a), DigitsToRound)
Dim part2 As Decimal = Math.Round((-b - d) / (2 * a), DigitsToRound)
'This line can be simplified, look up string interpolation if you are using VS 2017
MyAnswer = part1 & " , " & part2
Where DigitsToRound is indicated by the user somehow. A bit more code and it technically can be simplified down quite a bit but I was trying to be as explicit as possible.
**Edit:
Just realized there is a name discrepancy in your code. I'm assuming you meant MyAnswer instead of MyResult in the code snippet you posted. If I am mistaken then I apoligize.
** Edit #2:
How can I transfer the contents of these variables into another sub, so that when I call this other sub I'm able to round it to whatever d.p I want it to. e.g. 'would you like this into 3.d.p'... then it should output the answer in 3dp. I've tried to do it, but I tend to get 0 as an output
using a separate function for this seems like a bit of overkill but it could look something like this:
Public Function DoRounding(ByVal p_value as Decimal) as Decimal
Dim ret As Decimal = 0D
Dim DigitsToRound As Integer = 0
'This line prompts the user for input and attempts to parse the input into an integer field named DigitsToRound.
'If the parsing fails then whatever the user typed in was not an integer and we tell them we are unable to proceed. Default input is 1.
If Integer.TryParse(InputBox("How many digits would you like to round to?", "Decimal Places", 1), DigitsToRound) Then
ret = Math.Round(p_value, DigitsToRound)
Else
MessageBox.Show("Invalid Input Detected")
End If
Return ret
End Function
This would prompt the user every time the function was called which could be pretty annoying. It would be better to prompt them once and then pass the value that they enter around to whatever functions need it.
I am trying to create a macro for Word 2013 that does the following: the macro should capture the value of a cell of a word table and then add another value and paste the result in another cell of the same table.
My code so far is:
Sub prueba()
Dim a As String, b As String, c As String
Dim entero1 As Double, entero2 As Double
Dim resultado As Double
Dim tabla1 As Table
Set tabla1 = ActiveDocument.Tables(1)
a = tabla1.Cell(Row:=1, Column:=3).Range
entero1 = CDbl(a)
End Sub
But when I run it I get an error 13
To evaluate the error add the following two lines to validate if the data type obtained in "a" was a string
MsgBox (TypeName(a))
MsgBox (a)
And I got the following
I believe that the CDbl function does not finish converting the string to double because as they see the chain has a small square, what is not like to erase it so that the conversion is achieved.
Thank you very much for your help.
One way of extracting just the numeric portion of the Range would be to use the Val function, e.g.
entero1 = Val(a)
If the string a contained, for instance, 123.23XYZ4567 then Val(a) would return the number 123.23.
That should ensure that the non-numeric character that you are getting at the end of your Range is removed.
The answer provided by YowE3K is elegant and has my vote. For further information:
That 'small square' is the end of cell marker which is part of Cell.Range.Text (.Text is the default property returned when returning a range object is inappropriate).
To actually remove the end of cell marker (Chr(13) & Chr(7)) you can use something like this:
?CDbl(Replace$(Selection.Range.Cells(1).Range.Text, Chr(13) & Chr(7), vbNullString))
A possible advantage of this approach is that it may provide better opportunity to trap errors if you are only expecting numeric characters.
How do I get the length of character between beginning with space and ending with * Here is the image. Column B shows the total len before dasher(-) and my code
Sub xn()
Dim x As Integer
x = 1
If Worksheet("Sheet1").Range("A"& x).len(Right," ") Or _
Worksheet("Sheet1").Range("A"&x)len(Left,"-") Then
len(totallen)
End If
x = x + 1
End Sub
The code posted has multiple issues:
Worksheet is not a valid object - you need to use Worksheets.
.len is not a property of a Range object.
Even in .len was a property of a Range, you would need a
de-reference operator (aka '.') in here: Range("A"&x)len(Left,"-")
If you intend to use the function Len(), it only takes one argument.
You apparently are trying to loop, but you need to use either a For
or For Each loop - it won't loop automatically when you increment x
at the bottom of the sub.
Right is a function, but you're calling it without arguments and they are not optional.
Similarly, Left is a function, but you're also calling it without
the required arguments.
totallen is not declared anywhere, so Len(totallen) will assume
that totallen is a Variant (default for undeclared variables), then
cast it to a String, and then always return 0 because it has never
been given a value.
Anything else I may have missed.
The solution is to use the InStr function. It returns the location in a string of a given sub-string.
Sub xn()
Dim x As Long
Dim sheet As Worksheet
Set sheet = ActiveWorkbook.Worksheets("Sheet1")
For x = 1 To sheet.Range("A" & sheet.Rows.Count).End(xlUp).Row
sheet.Cells(x, 2) = InStr(1, sheet.Cells(x, 1), "-") - 1
Next x
End Sub
I'd also recommend taking a look at the MSDN article on Looping Through a Range of Cells (2003 vintage, but still valid), and Error Finding Last Used cell In VBA.