I have a query that compares a value. Both values are equal. Data types are double. However, the result is always false. Have you encountered the same scenario?
I tried to round off the data before comparing it and I got the correct results. What do you think is the cause of this issue?
This is a well known side effect of floating numbers.
You have several options. Test for a difference or convert to other data type:
Where Abs(Field1 - Field2) < 0.00001 (or whatever value you consider equal)
Where CCur(Field1) = CCur(Field2)
Where CDec(Field1) = CDecCur(Field2)
Confirm that the values are equal for both variables. Double is floating point with 15 decimal precision, I think. If your display is showing your double variables with rounding, they may appear equal but in fact they may not be equal.
I would recommend you to do Round(variable1, 4) and compare it with Round(variable2, 4). Try testing your variables with a test routine like this:
Public Sub SendDoubles()
Dim a As Double
Dim b As Double
a = 10.0000000001
b = 10.000000001
TestDoubles a, b
End Sub
Public Sub TestDoubles(a As Double, b As Double)
MsgBox CStr(a) & vbCrLf & CStr(b)
MsgBox a = b
MsgBox Round(a, 4) = Round(b, 4)
End Sub
Notice that CStr will convert your double to string so that you can visualize both variable's value. The 2nd MsgBox attempts to compare them as-is and the third one compares them with rounding.
Related
In MS Access I have a table with a Short Text field named txtPMTaskDesc in which some records contains numbers, and if they do, at different positions in the string. I would like to recover these numbers from the text string if possible for sorting purposes.
There are over 26000 records in the table, so I would rather handle it in a query over using VBA loops etc.
Sample Data
While the end goal is to recover the whole number, I was going to start with just identifying the position of the first numerical value in the string. I have tried a few things to no avail like:
InStr(1,[txtPMTaskDesc],"*[0-9]*")
Once I get that, I was going to use it as a part of a Mid() function to pull out it and the character next to it like below. (its a bit dodgy, but there is never more than a two-digit number in the text string)
IIf(InStr(1,[txtPMTaskDesc],"*[0-9]*")>0,Mid([txtPMTaskDesc],InStr(1,[txtPMTaskDesc],"*[0-9]*"),2)*1,0)
Any assistance appreciated.
If data is truly representative and number always preceded by "- No ", then expression in query can be like:
Val(Mid(txtPMTaskDesc, InStr(txtPMTaskDesc, "- No ") + 5))
If there is no match, a 0 will return, however, if field is null, the expression will error.
If string does not have consistent pattern (numbers always in same position or preceded by some distinct character combination that can be used to locate position), don't think can get what you want without VBA. Either loop through string or explore Regular Expressions aka RegEx. Set reference to Microsoft VBScript Regular Expressions x.x library.
Function GetNum(strS AS String)
Dim re As RegExp, Match As Object
Set re = New RegExp
re.Pattern = "[\d+]+"
Set Match = re.Execute(strS)
GetNum = Null
If Match.Count > 0 Then GetNum = Match(0)
End Function
Input of string "Fuel Injector - No 1 - R&I" returns 1.
Place function in a general module and call it from query.
SELECT table.*, GetNum(Nz(txtPMTaskDesc,"")) AS Num FROM table;
Function returns Null if there is no number match.
Well, does the number you want ALWAYS have a - No xxxx - format?
If yes, then you could have this global function in VBA like this:
Public Function GNUM(v As Variant) As Long
If IsNull(v) Then
GNUM = 0
Exit Function
End If
Dim vBuf As Variant
vBuf = Split(v, " - No ")
Dim strRes As String
If UBound(vBuf) > 0 Then
strRes = Split(vBuf(1), "-")(0)
GNUM = Trim(strRes)
Else
GNUM = 0
End If
End Function
Then your sql will be like this:
SELECT BLA, BLA, txtPMTaskDesc, GNUM([txtPMTaskDesc] AS TaskNum
FROM myTable
So you can create/have a public VBA function, and it can be used in the sql query.
It just a question if " - No -" is ALWAYS that format, then THEN the number follows this
So we have "space" "-" "space" "No" "space" "-" -- then the number and the " -"
How well this will work depends on how consistent this text is.
I've been searching and searching and my Google-fu has failed me. I'm trying to convert an encoded number from base-32 to decimal using either expressions or a macro, but I'm not finding anything. I know Excel has the "Decimal" function, I've been hoping that I could stumble onto something similar.
I'm reluctant to use VBA as I don't want to spend time re-learning the language right now and I'm worried that my organization will flag it as potentially dangerous (which could kill my attempts at making any databases).
With an input of "16O9E55"
I expect a result of 1300543653.
I should clarify that this is "base32hex" according to Wikipedia. It's 0-9, A-V. It's only 7 characters of base-32 that needs to convert to 10 digits of decimal. My use case is decoding a barcode into the data I need.
I doubt this can be accomplished without VBA. Consider code adapted from https://www.excelbanter.com/excel-worksheet-functions/150198-formulat-convert-base-32-decimal.html
Public Function Base32ToDec(Num As String) As Variant
Static Digits As String
Dim i As Integer
Dim myIndex As Integer
Dim myStr As String
Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUV"
For i = Len(Num) To 1 Step -1
myStr = Mid(Num, i, 1)
myIndex = InStr(Digits, myStr) - 1
Base32ToDec = Base32ToDec + myIndex * 32 ^ (Len(Num) - i)
Next i
End Function
According to Wikipedia, Base32 uses a 32-character set comprising the twenty-six upper-case letters A–Z, and the digits 2–7. The variant base32hex starts with 0 - 9 and uses the letters A to V.
If only numbers with a limited range have been encoded, you can decode them easily with VBA, otherwise you would have to return an array of bytes and process it further.
You write, that you have up to 10 decimal digits. The question is, what the maximum number is. The Long type can store numbers up to 2,147,483,647. This are ten digits; however, with 10 digits you could store a number as big as 9,999,999,999.
Therefore, the following function returns the number as Double. If you know that your number will never exceed 2,147,483,647, then you can exchange the Double type by Long for the sum variable and the function return type.
Public Function DecodeBase32hex(ByVal encoded As String) As Double
Dim ch As String
Dim sum As Double
Dim d As Long, i As Long
For i = 1 To Len(encoded)
ch = Mid$(encoded, i, 1)
If ch >= "A" And ch <= "Z" Then
d = Asc(ch) - Asc("A") + 10
ElseIf ch >= "0" And ch <= "9" Then
d = Asc(ch) - Asc("0")
Else
Exit For 'E.g. padding charachters
End If
sum = 32 * sum + d
Next i
DecodeBase32hex = sum
End Function
Test in Access' immediate window:
?DecodeBase32hex("16O9E55")
1300543653
I'm trying to figure out a solution on how to concatenate strings from about 15 different options. Each result comes from a checkbox that is selected based on the state a person has lived in within a certain area.
I know how to turn the checkbox option into a text result. What I'm looking for is how to take these text results, combine them, then ignore null results so there isn't any weird spacing or formatting.
In short, if someone select 3 of the 15 results it would combine the 3 results cleanly and ignore the rest. Example would be: FL, CA, NY
There are, of course, multiple ways that this can be achieved, and since you didn't provide any code or examples of how you are attempting to do this, I will provide two options.
1 - You can concatenate the values using a combination of the & and + operators.
For example, let's say you have 15 checkboxes, all named similarly like chkState01, chkState02 ... through chkState15. And for the simplicity of my sample code, let's assume that when referencing the checkbox control directly in code as chkState01 that it will return either the 2 letter string abbreviation of the State it represents (i.e. NY) if the checkbox was checked, or it will return Null if the checkbox was not checked. With that, you could get your results in 2 ways:
Option A
StateList = (chkState01 + ",") & (chkState02 + ",") & (chkState03 + ",") ....
If those 3 check boxes returned the following values
chkState01 = "NY"
chkState02 = Null
chkState03 = "FL"
Then the result of that concatenation code would be:
NY,FL,
Notice that the string ends with an extra comma (,) and always would since you can't know ahead of time how many of the checkboxes will be checked. You would then need to trim that comma from your list before using it most likely.
Option B
'Create the list with a trailing comma that will need removed
Dim x as Integer
For x = 1 to 15
StateList = StateList & (Me("chkState" & Format(x, "00")) + ",")
Next x
or, you could do:
'Create the list without a trailing comma
Dim x as Integer
For x = 1 to 15
If Not IsNull(Me("chkState" & Format(x, "00"))) Then
If Len(StateList) > 0 Then
StateList = StateList & "," & Me("chkState" & Format(x, "00"))
Else
StateList = Me("chkState" & Format(x, "00"))
End If
End If
Next x
Notice that you can reference a control on a form by "generating" the name of that control as a string and referencing it in the Me("yourcontrolname") format. This is one advantage to naming controls that are similar in a fashion that lends itself to a looping structure like this. The Format command formats the number returned by x as a 2 digit with leading zeros i.e. 1 becomes 01
Further, using & to concatenate two items, where at least one of them is a string, will always result in a string output (Null & "S" = "S"). However, using the + to concatenate two items, where at least one of them is a Null, will always result in Null output (Null + "S" = Null). Hence the checkboxes where the value returns Null does not cause additional commas to be included in the result.
2 - You can write more complicated code to dynamically loop through the checkboxes and build the output list.
More likely, you are going to need to use additional code to determine which checkbox is which state abbreviation and to return the correct string value. Maybe you made the state abbreviation part of the checkbox name i.e. chkState_NY, chkState_FL or maybe you have put the abbreviation in the Tag property of each checkbox.
Let's say you used special control naming chkState_NY, chkState_FL. You could do the following:
Dim ctl as Access.Control
For Each ctl in Me.Controls
If ctl.Name Like "chkState_??" Then
If ctl.Value = True Then
If Len(StateList) > 0 Then
StateList = StateList & "," & Right(ctl.Name,2)
Else
StateList = Right(ctl.Name,2)
End If
End If
End If
Next ctl
In my Access query, I have the query using a VBA function to figure the value that goes in the query field.
In the form, if the stringval textbox has a value, then I want to compute it, but if not, it should remain empty (null).
Function GetValue(stringval, numval)
Dim result
stringval= stringval & ""
result= IIf(stringval<> "", numval* 1.5, Null)
GetValue = Int(result)
End Function
Now, I have a form that uses this query, and on the form is a textbox that displays the query value. I want the value to be formatted with commas in the numbers for easy reading. Everything I've tried so far does not show any commas.
I've tried:
used Standard for the Format > Formatfor the textbox (in properties)
putting #,###.### in the textbox Format value
putting #,##0.0## in the textbox Format value
changing Data > Text Format but it only gives me Plain Text and Rich Text - no option for numbers.
returning a double from the function
Note: if I don't use a custom VBA function, and write the formula directly into the query, then it does display commas. But when I move the formula into the function then the commas are lost.
What do I do?
[update]
I tried Gustav's solutions and since they didn't work for me, I added those as items to my "what I've tried" list above.
Also, if I look at the query in datasheet view, the number values sort alphabetically instead of by the size of the value. When I used the forumulae directly in the query instead of using functions, it sorted by the value of the number. I hope this is a clue.
Numbers carries no format. A format is applied when displayed only.
But be sure to return a Double if not Null:
Function GetValue(stringval, numval)
Dim result
If stringval & "" <> "" Then
result = Int(CDbl(numval) * 1.5)
Else
result = Null
End If
GetValue = result
End Function
Then apply your Format to the textbox
#,##0.0##
Or force a formatted string to be returned:
If stringval & "" <> "" Then
result = Format(Int(CDbl(numval) * 1.5), "#,##0.0##")
Else
result = Null
End If
and skip formatting of the textbox.
The solution is this: the function has to be declared as a double.
That allows the query's datasheet view to know it is displaying numbers - and so you can set the field's format to Standard for the comma to display. This also allows the form to know it has a number and it will display the comma there, too. I knew it had to do with double, but didn't realize before that the function needed to be declared as such.
Function GetValue(stringval, numval) as double '<----THIS!!!!
Dim result
If stringval & "" <> "" Then
result = numval * 1.5
Else
result = 0 `<--can't return null here; use nz function in control source for textbox
End If
GetValue = int(result) 'to remove decimals
End Function
The problem I was having was in some of my functions I need to return double or null, because I wanted textboxes to remain blank if they contained no data. Now, at least I know how to make the numbers generated by functions to display commas.
And here is how to deal with the fact that you can't return null as the value of a double. The function is originally from here.
Put this function in a module so it is public, and then in the control source for the textbox, instead of just putting the field value, put Zn(fieldvalue). This works like a charm (although using functions in the control source seems to have a delay on the form display). This way you can keep the underlying value as a double and still get commas to display in both the form and the query whilst keeping the field blank if necessary.
Public Function Zn(pvar)
' Return null if input is zero or ""
If IsNull(pvar) Then
Zn = Null
ElseIf IsNumeric(pvar) Then
If pvar = 0 Then
Zn = Null
Else
Zn = pvar
End If
Else
If Len(pvar) = 0 Then
Zn = Null
Else
Zn = pvar
End If
End If
End Function
After applying the unpivot procedure, I have an Amount column that has blanks and other characters ( like "-"). I would like to convert those non-numberic values to zero. I use replace procedure but it only converts one at the time.
Also, I tried to use the following script
/**
Public Overrides Sub Input()_ProcessInputRows(ByVal Row As Input()Buffer)
If Row.ColumnName_IsNull = False Or Row.ColumnName = "" Then
Dim pattern As String = String.Empty
Dim r As Regex = Nothing
pattern = "[^0-9]"
r = New Regex(pattern, RegexOptions.Compiled)
Row.ColumnName = Regex.Replace(Row.ColumnName, pattern, "")
End If
End Sub
**/
but i'm getting error.I don't much about script so maybe I placed in the wrong place. The bottom line is that I need to convert those non-numberic values.
Thank you in advance for your help.
I generally look at regular expressions as a great way to introduce another problem into an existing one.
What I did to simulate your problem was to write a select statement that added 5 rows. 2 with valid numbers, the rest were an empty string, string with spaces and one with a hyphen.
I then wired it up to a Script Component and set the column as read/write
The script I used is as follows. I verified there was a value there and if so, I attempted to convert the value to an integer. If that failed, then I assigned it zero. VB is not my strong suit so if this could have been done more elegantly, please edit my script.
Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
' Ensure we have data to work with
If Not Row.ColumnName_IsNull Then
' Test whether it's a number or not
' TryCast doesn't work with value types so I'm going the lazy route
Try
' Cast to an integer and then back to string because
' my vb is weak
Row.ColumnName = CStr(CType(Row.ColumnName, Integer))
Catch ex As Exception
Row.ColumnName = 0
End Try
End If
End Sub