What's the best way to test two Singles for equality in VB6?
I want to test two Single values for equality to 7 significant figures.
This MSDN article recommends using something like
If Abs(a - b) <= Abs(a / 10 ^ 7) Then
valuesEqual = True
End If
However, that can fail for certain values, e.g.
Public Sub Main()
Dim a As Single
Dim b As Single
a = 0.50000005
b = 0.50000014
Debug.Print "a = " & a
Debug.Print "b = " & b
Debug.Print "a = b: " & (a = b)
Debug.Print "SinglesAreEqual(a, b): " & SinglesAreEqual(a, b)
// Output:
// a = 0.5000001
// b = 0.5000001
// b = b: False
// SinglesAreEqual(a, b): False
End Sub
Private Function SinglesAreEqual(a As Single, b As Single) As Boolean
If Abs(a - b) <= Abs(a / 10 ^ 7) Then
SinglesAreEqual = True
Else
SinglesAreEqual = False
End If
End Function
The simplest way I've found of getting the result I need is to convert the values to strings, but seems horribly ugly:
Private Function SinglesAreEqual(a As Single, b As Single) As Boolean
SinglesAreEqual = (Str$(a) = Str$(b))
End Function
Are there any better ways?
I maintain a CAD/CAM application and I have to deal with floating point numbers all the time. I have a function that I call fComp that I pass a floating point value when I need to test for equality. fComp call a rounding function set to a certain level of precision. For our system I round to 6 decimal places. Yours may need higher or get away with lower it depends on the application.
The fComp Function exists so I have one spot to change the rounding factor used in these calculations. This proved handy a couple of years back when we started manufacturing higher precision machines.
Public Function pRound(ByVal Value As Double, ByVal Power As Double) As Double
Dim TempValue As Double
Dim tSign As Double
TempValue = Value
tSign = TempValue
TempValue = Abs(TempValue)
TempValue = TempValue * 10 ^ (Power * -1)
TempValue = Fix(TempValue + 0.5)
TempValue = TempValue / 10 ^ (Power * -1)
pRound = TempValue * Sign(tSign)
End Function
To round to the 6th decimal place you go
RoundedNumber = pRound(MyValue, -6)
Negative is to the right of the decimal place positive to the left.
Instead if rounding and testing for equality, you can take the difference of two numbers and compare that with a factor
If Abs(a - b) < 0.000001 Then
You can adjust the 0.000001 to whatever resolution you need
I don't believe you can use the single data type to that many significant figures. You would need to use double instead:
Dim a As Single
Dim s As String
s = "0.50000005"
a = 0.50000005
Debug.Print s & " " & a
The above outputs:
0.50000005
0.5000001
Related
Hello i am trying to make a code that removes the last digit from an integer example
int num = 1234
to be
int num 123
I did the same code in C# but i am failing to do it on vb6 this is the c# code.
num = num.Remove(num.length -1)
on vb6 i tried something like this
num = Len(num) -1
but all it does is from
num = 1234
it makes it
num = 1233
or just shows
num = 3
since removed the 4th digit
You are on the right path. The key missing step is to convert the number to a string before manipulating it.
Option Explicit
Private Sub Command1_Click()
Dim num As Integer
Dim s As String
num = 1234
s = CStr(num) 'convert number to a string
s = Left(s, Len(s) - 1) 'remove last digit
num = CInt(s) 'convert string back to a number
Debug.Print num
End Sub
The error in your code is using Len on an Integer. From the documentation:
Returns a Long containing the number of characters in a string or the
number of bytes required to store a variable.
You can simply divide your number by 10:
Function TrimInteger(number As Integer) As Integer
TrimInteger = Int(number / 10)
End Function
Int is used to remove the fractional part of the division result and return the resulting integer value.
You can also declare number and the Function's return type as a Long instead of an Integer to support larger numbers:
Function TrimNumber(number As Long) As Long
TrimNumber = Int(number / 10)
End Function
All,
I'm intermediate for VB6. I want to get remainder from divided two doubles. I used ,
Dim a As Double, b As Double, result As Double
b = 8333.33
a = 58333.31
result = a - (b * Fix(a / b))
result should be 0. But it is not.
a/b =7 and no remain. So Fix(a / b) should be 7. But Fix(a / b)=6, Why?
If you use this as a workaround I think you will get the result you expect:
Dim a As Double, b As Double, intermediate As Double, fixed1 As Double, fixed2 As Double
b = 8333.33
a = 58333.31
intermediate = a / b
fixed1 = Fix(intermediate)
fixed2 = Fix(a / b)
Debug.Print intermediate ' 7
Debug.Print fixed1 ' 7
Debug.Print fixed2 ' 6
Documentation for Fix says:
Int, Fix functions
Returns the integer portion of a number.
... Remarks Both Int and Fix remove the fractional part of number and
return the resulting integer value.
(that is from VBA docs but should be the same for VB6).
Apparently when VB evaluates a / b within the context of the function call it results in a floating point value very slightly < 7.
BTW, another trick to get it working - depending on the floating point numbers you need to work with - is to use Currency instead of Double
Dim a As Currency, b As Currency
b = 8333.33
a = 58333.31
Debug.Print Fix(a / b) ' Returns 7
Debug.Print a - (b * Fix(a / b)) ' Returns 0
I want to take a number and convert it into lowercase a-z letters using VBScript.
For example:
1 converts to a
2 converts to b
27 converts to aa
28 converts to ab
and so on...
In particular I am having trouble converting numbers after 26 when converting to 2 letter cell names. (aa, ab, ac, etc.)
You should have a look at the Chr(n) function.
This would fit your needs from a to z:
wscript.echo Chr(number+96)
To represent multiple letters for numbers, (like excel would do it) you'll have to check your number for ranges and use the Mod operator for modulo.
EDIT:
There is a fast food Copy&Paste example on the web: How to convert Excel column numbers into alphabetical characters
Quoted example from microsoft:
For example: The column number is 30.
The column number is divided by 27: 30 / 27 = 1.1111, rounded down by the Int function to "1".
i = 1
Next Column number - (i * 26) = 30 -(1 * 26) = 30 - 26 = 4.
j = 4
Convert the values to alphabetical characters separately,
i = 1 = "A"
j = 4 = "D"
Combined together, they form the column designator "AD".
And its code:
Function ConvertToLetter(iCol As Integer) As String
Dim iAlpha As Integer
Dim iRemainder As Integer
iAlpha = Int(iCol / 27)
iRemainder = iCol - (iAlpha * 26)
If iAlpha > 0 Then
ConvertToLetter = Chr(iAlpha + 64)
End If
If iRemainder > 0 Then
ConvertToLetter = ConvertToLetter & Chr(iRemainder + 64)
End If
End Function
Neither of the solutions above work for the full Excel range from A to XFD. The first example only works up to ZZ. The second example has boundry problems explained in the code comments below.
//
Function ColumnNumberToLetter(ColumnNumber As Integer) As String
' convert a column number to the Excel letter representation
Dim Div As Double
Dim iMostSignificant As Integer
Dim iLeastSignificant As Integer
Dim Base As Integer
Base = 26
' Column letters are base 26 starting at A=1 and ending at Z=26
' For base 26 math to work we need to adjust the input value to
' base 26 starting at 0
Div = (ColumnNumber - 1) / Base
iMostSignificant = Int(Div)
' The addition of 1 is needed to restore the 0 to 25 result value to
' align with A to Z
iLeastSignificant = 1 + (Div - iMostSignificant) * Base
' convert number to letter
ColumnNumberToLetter = Chr(64 + iLeastSignificant)
' if the input number is larger than the base then the conversion we
' just did is the least significant letter
' Call the function again with the remaining most significant letters
If ColumnNumber > Base Then
ColumnNumberToLetter = ColumnNumberToLetter(iMostSignificant) & ColumnNumberToLetter
End If
End Function
//
try this
function converts(n)
Dim i, c, m
i = n
c = ""
While i > 26
m = (i mod 26)
c = Chr(m+96) & c
i = (i - m) / 26
Wend
c = Chr(i+96) & c
converts = c
end function
WScript.Echo converts(1000)
I am trying to round up numbers in a legacy application which I had coded in VB6 to obtain the following outcomes:
2.53 should be 2.60
2.55 should be 2.60
2.56 should be 2.60
2.50 should remain 2.50
2.501 should be 2.50
2.505 should be 2.60
I have tried to use the suggested User-Defined Rounding functions by Microsoft
http://support.microsoft.com/kb/196652/en-gb
The closest I arrived to is the Asymup function
Function AsymUp(ByVal X As Double, _
Optional ByVal Factor As Double = 1) As Double
Dim Temp As Double
Temp = Int(X * Factor)
AsymUp = (Temp + IIf(X = Temp, 0, 1)) / Factor
End Function
I am testing this procedure by calling the function as follows:
Text1.Text = AsymUp(Val(Text1.Text), 10)
But this is not producing the desired results because 2.60 for example becomes 2.7 when I want it to remain 2.6. Strangely enough 2.0 also becomes 2.1, implying that the function is not working well.
How can I correct this to acheive the desired results
Private Function AsymUp(ByVal D As Double, Optional Precision As Double = 1) As Double
Precision = CDbl("1" & String$(Precision, 48))
D = D * Precision
If Int(D) <> D Then
AsymUp = (Int(D) + 1) / Precision
Else
AsymUp = D / Precision
End If
End Function
Visual Basic will always automatically truncate trailing zeros from Double and Single data types. Therefore, unless a function is designed to pass the number back as a string, along with the extra zero(s) appended, it will be impossible to retain trailing zeros of a particular precision.
Bonus tip: The IIf function should not be used where performance is of concern, as both the falsepart and truthpart parameters are always evaluated, regardless of the arguments passed to them.
String manipulation method (retains zeros):
Private Function AsymUp(ByVal D As Double, Optional Precision As Double = 1) As String
Dim P As Double, Z As Long
P = CDbl("1" & String$(Precision, 48))
D = D * P
If Int(D) <> D Then
D = (Int(D) + 1) / P
Else
D = D / P
End If
AsymUp = CStr(D)
Z = Precision - (Len(AsymUp) - InStr(AsymUp, "."))
If Z > 0 Then AsymUp = AsymUp & String$(Z, 48)
End Function
This will obviously not be as efficient to run as my other answer, but it's the only way to keep any trailing zeros intact.
Please note that the function will always return the number as a string. You may need to convert the string back to a decimal number to continue using the number with additional mathematics in your program.
I've resolved the issue using the following code:
Private Sub Command1_Click()
roundnumber = Val(Text1.Text)
Text1.Text = Round(roundnumber, 2)
Text1.Text = AsymUp(Val(Text1.Text), 10)
End Sub
Function AsymUp(ByVal X As Double, _
Optional ByVal Factor As Double = 1) As Double
Dim Temp As Double
Temp = Int(X * Factor)
AsymUp = (Temp + IIf((X * Factor) = Temp, 0, 1)) / Factor
End Function
This is what I was after not precision to 10dp as suggested in this post.
I would like to know if there is a way to get a random letter (from A-Z)
Thanks for any help.
I think this is what you're looking for. Generate a Random Letter in ASP:
Function RandomNumber(LowNumber, HighNumber)
RANDOMIZE
RandomNumber = Round((HighNumber - LowNumber + 1) * Rnd + LowNumber)
End Function
Assign the function to a variable and pass in the LowNumber (26) and
the HighNumber (97) and convert the value returned to the character it
represents:
RandomLetter = CHR(RandomNumber(97,122))
You'll want your range to be between 65 and 90 (A and Z) for capital letters.
Roger Baretto's answer fixed with Cem's hint ))
Function RandomString(iSize)
Const VALID_TEXT = "abcdefghijklmnopqrstuvwxyz1234567890"
Dim Length, sNewSearchTag, I
Length = Len(VALID_TEXT)
Randomize()
For I = 1 To iSize
sNewSearchTag = sNewSearchTag & Mid(VALID_TEXT, Int(Rnd()*Length + 1), 1)
Next
RandomString = sNewSearchTag
End Function
Here is another way to look at it without using an if/switch.
String alphabet = "abcdefghijklmnopqrstuvwxyz";
Random rand = new Random();
char randomCharacter = alphabet[rand.Next(0, 25)];
I came to a solution that you can have easy control of what are the valid values for your generator.
Function CreateRandomString(iSize)
Const VALID_TEXT = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
Dim sNewSearchTag
Dim I
For I = 0 To iSize
Randomize
sNewSearchTag = sNewSearchTag & Mid(VALID_TEXT,Round(Rnd * Len(VALID_TEXT)),1)
Next
CreateRandomString = sNewSearchTag
End Function
use a random number... like this:
Function RandomNumber(LowNumber, HighNumber)
RANDOMIZE
RandomNumber = Round((HighNumber - LowNumber + 1) * Rnd + LowNumber)
End Function
and then use it from 1-26, use "if" or switch, to get the letter.
Rogerio's answer is fine but Round(Rnd * Len(VALID_TEXT)) can be 0 and Mid cannot start from 0. Fix it if you want to use this function.