Recursive function to convert from decimal to binary - excel

My code has a problem with conversion of the number 3
I would like to write a function which converts a decimal number into a binary one. The principle of recursion must be used. I have already written the following code.
Function recursive(number As Integer) As String
Dim result As String
If number > 0 Then
Dim binaryNumber As String
Dim digit As Integer
binaryNumber = recursive(number / 2)
digit = number Mod 2
result = result & binaryNumber & digit
End If
recursive = result
End Function
Right result:
Input: 10
Output: 1010
Wrong result:
Input: 3
Output: 101
It also works reasonably well, but I get a wrong result when I try to convert the decimal number 3. Where is the error?

Function recursive(number As Integer) As String
Dim result As String
If number > 0 Then
Dim binaryNumber As String
Dim digit As Integer
digit = number Mod 2
number = Int(number / 2)
binaryNumber = recursive(number)
result = result & binaryNumber & digit
End If
recursive = result
End Function

Related

VBA generate a code

there. I made this code that replaces a character for two number (e.g. 0 = 10; 1 = 11; 2 = 12; ...) and everything works fine except for the first element (the zero element). So, if I put "010a4" string on cell A1 and use my formula "=GENERATECODE(A1)", my expected return value is "1011102014" but I've got a "110111102014" string. So, only zero value occur this error and I can't figured out why. Any thoughts?
My code:
Function GENERATECODE(Code As String)
Dim A As String
Dim B As String
Dim i As Integer
Const AccChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
Const RegChars = "1011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071"
For i = 1 To Len(AccChars)
A = Mid(AccChars, i, 1)
B = Mid(RegChars, 2 * i - 1, 2)
Code = Replace(Code, A, B)
Next
GENERATECODE = Code
End Function
Your problem is that your code first change each 0 to 10 then each 1 to 11. So each 0 give you 10 then 110.
If you want to keep the same kind of algorithm (which might not be a good choice), you need to change AccChars and RegChars so that a character is never replaced by a string that can give a character found later on the AccChars String. In your case just replace Const AccChars = "012 ... per Const AccChars = "102 ... and Const RegChars = "101112 ... per Const RegChars = "111012 ...
But it might be better to change your algorithm altogether. I would first suggest to not use in place editing of the string, but rather to use 2 Strings.
In addition to being incorrect, your current code is inefficient since it involves scanning over the code string multiple times instead of just once. Simply scan over the string once, gathering the substitutions into an array which is joined at the end:
Function GENERATECODE(Code As String) As String
Dim codes As Variant
Dim i As Long, n As Long
Dim c As String
n = Len(Code)
ReDim codes(1 To n)
For i = 1 To n
c = Mid(Code, i, 1)
Select Case c
Case "0" To "9":
codes(i) = "1" & c
Case "a" To "z":
codes(i) = Asc(c) - 77
Case "A" To "Z":
codes(i) = Asc(c) - 19
Case Else:
codes(i) = "??"
End Select
Next i
GENERATECODE = Join(codes, "")
End Function
Example:
?generatecode("010a4")
1011102014
The point of the two offsets is that you want "a" to map to 20 and "A" to map to 46. Note Asc("a") - 77 = 97 - 77 and Asc("A") - 19 = 65-19 = 46.

Split a float number at decimal point using Excel VBA

I am trying to split a number at the decimal point and store the two parts in two strings.
E.g. 12.345 to be stored as S1 = 12 and S2 = 345
E.g. 12.3450 to be stored as S1 = 12 and S2 = 345
Can someone guide me with excel VBA for this?
You can use the split function to do this:
Dim splitnnum
Dim num as Double
num = 111.222
splitnum = Split(Str(num), ".")
s1 = splitnum(0)
s2 = splitnum(1)
This will split at the decimal and assign each part to a string.
Dim x as Variant
Dim num1 as String, num2 as String
x = Split(Str(12.345), ".")
num1 = x(Lbound(x,1))
num2 = x(Ubound(x,1))
The easiest way to do it would likely be something like:
Dim lHolder as Double
Dim lWhole as Double
Dim lRemain as Double
lHolder = 12.345 ' Or whatever variable you have your number stored in
lWhole = Fix(lHolder)
lRemain = lHolder - lWhole
You can then round lRemain as needed. For example
Round(lRemain, 5) ' Rounds out to 5 decimal places
Will return .345
This breaks your number at decimal point
mynumber = 111.222
splitmynumber = Split(Str(mynumber ), ".")
'This is an array storing 2 number
numberbeforedecimal = val(splitmynumber (0))
numberafterdecimal = val(splitmynumber (1))
Answer : 111 & 222

Where is the decimal coming from in this calculation?

I have a function that calcuates a BOL number and only take the first 10 digits.
Here's the code.
Public Function GENERATEBOLNUMBER(iYearSuffix As Integer, _
sFromZipcode As String, _
sToZipCode As String, _
iWeight As Integer, _
iShowID As Integer) As String
Application.ScreenUpdating = False
GENERATEBOLNUMBER = VBA.Left(7 & _
WorksheetFunction.RoundUp(VBA.Right(sFromZipcode, 5) _
* VBA.Right(sToZipCode, 5) * iWeight * (iShowID + 1234), 0), 10)
Application.ScreenUpdating = True
End Function
And here are the values I'm passing it. 7 for the iYearSuffix, 78224 for sFromZipcode and 78224 for sToZipCode, 410 as the iWeight, and 1 as the iShowID. All of this calculates to 3098352701017600, so the final string should be 7309835270, which is the 7 included as the first digit and the following 9 digits.
Where is the decimal coming from? The answer I'm getting is: 73.0983527.
You're mixing string handling, numeric manipulation, implicit casts between strings and numbers, VBA, WorksheetFunction, and gigantic numbers. What could possibly go wrong?
If you're going to write a UDF in VBA, write it in VBA. The only data type large enough to store your result is going to be a Decimal, so you'll have to declare it as a Variant and explicitly cast the calculation to force it to coerce:
Public Function GENERATEBOLNUMBER(yearSuffix As Integer, fromZip As String, _
toZip As String, weight As Integer, _
showId As Integer) As String
Dim result As Variant
'Calculate intermediary result.
result = CDec(Right$(fromZip, 5)) * CDec(Right$(toZip, 5)) * weight * (showId + 1234)
'Shift the decimal place 7 places to the left:
result = result / 10 ^ 7
'Skip the RoundUp call - it wasn't doing anything because your result was an integer.
'Strip the non-integer portion:
result = Fix(result)
'Cast to a string an concatenate the "7" onto the start:
GENERATEBOLNUMBER = "7" & CStr(result)
End Function

Replace a string with a format string

I have the following input:
Dim str_format as string = "XXXXX00000"
Dim str as string = "INV"
Dim int as integer = "56"
How can I replace XXXXX with INV and replace 00000 with 56?
For the example above the result should be INVXX00056.
X can only replace with alphabet and 0 can only replace with integer, if str has more than five alphabet. The extra alphabets will be thrown away because str_format only has five X. The same algorithm is true for the integer.
Example 2
Dim str_format as string = "XXX00000"
Dim str as string = "ABCD"
Dim int as integer = 654321
Expected result: ABC54321
Process:
1. ABCD XXX00000 654321
2. ABC DXX000006 54321
3. AB CDX00065 4321
4. A BCD00654 321
5. ABC06543 21
6. ABC65432 1
7. ABC54321
As Spidey mentioned... show some code. That said the process you describe is a bit long-winded.
The Letter part of the solution can be done by grabbing the first 3 characters of str using Left(str,3) this will bring in the leftmost 3 character (if there are less it will get what is there). Then check that you have 3 characters using str.Length(). If the length is less than 3 then append the appropriate number of 'X'.
The Numeric part can be done in a similar way. Your int is actually a string in your code above. If it was a real integer you can cast it to string. Use Right(int,5). Again check to see you have 5 digits and if not prepend with appropriate number of 0.
Have a go... if you run into problems post your code and someone is bound to help.
UPDATE
As there have been actual answers posted here is my solution
Function FormatMyString(str As String, num as String) As String
Dim result As String
result = Left(str,3).PadRight(3, "X"c).ToUpper() & Right(num,5).PadLeft(5, "0"c)
Return result
End Function
UPDATE 2
based on Wiktors answer... made an amendment to my solution to cope with different formats
Function FormatMyString(str As String, num as String, alpha as Integer, digits as Integer) As String
Dim result As String
result = Left(str, alpha).PadRight(alpha, "X"c).ToUpper() & Right(num, digits).PadLeft(digits, "0"c)
Return result
End Function
To use...
FormatMyString("ABCDE", "56",3 5) will return ABC00056
FormatMyString("ABCDE", "123456",4 3) will return ABCD456
FormatMyString("AB", "123456",4 3) will return ABXX456
Here is a possible solution that just uses basic string methods and PadLeft/PadRight and a specific method to count occurrences of specific chars in the string. It assumes the format string can only contain X and 0 in the known order.
Public Function CountCharacter(ByVal value As String, ByVal ch As Char) As Integer
Return value.Count(Function(c As Char) c = ch)
End Function
Public Sub run1()
Dim str_format As String = "XXXXX00000" '"XXX00000"
Dim str As String = "INV"
Dim int As Integer = 56 ' ABC54321
Dim xCnt As Integer = CountCharacter(str_format, "X")
Dim zCnt As Integer = CountCharacter(str_format, "0")
Dim result As String
If xCnt > str.Length Then
result = str.PadRight(xCnt, "X")
Else
result = str.Substring(0, xCnt)
End If
If zCnt > int.ToString().Length Then
result = result & int.ToString().PadLeft(zCnt, "0")
Else
result = result & int.ToString().Substring(int.ToString().Length-zCnt
End If
Console.WriteLine(result)
End Sub
Output for your both scenarios is as expected.
Take a look at this sample
Dim str_format As String = str_format.Replace("XXX", "ABC")
Msgbox(str_format )
As we assume that the X is 3 only. I dont want to give you more it is a start and everything will be easy.
If that kind of format is fix I mean the number of X will go or down then you can make a conditional statement based on the length of string

Truncating Double with VBA in excel

I need to truncate the amount of decimal places of my double value for display in a textbox. How would one achieve this with vba?
If you want to round the value, then you can use the Round function (but be aware that VBA's Round function uses Banker's rounding, also known as round-to-even, where it will round a 5 up or down; to round using traditional rounding, use Format).
If you want to truncate the value without rounding, then there's no need to use strings as in the accepted answer - just use math:
Dim lDecimalPlaces As Long: lDecimalPlaces = 2
Dim dblValue As Double: dblValue = 2.345
Dim lScale = 10 ^ lDecimalPlaces
Dim dblTruncated As Double: dblTruncated = Fix(dblValue * lScale) / lScale
This yields "2.34".
You can either use ROUND for FORMAT in VBA
For example to show 2 decimal places
Dval = 1.56789
Debug.Print Round(dVal,2)
Debug.Print Format(dVal,"0.00")
Note: The above will give you 1.57. So if you are looking for 1.56 then you can store the Dval in a string and then do this
Dim strVal As String
dVal = 1.56789
strVal = dVal
If InStr(1, strVal, ".") Then
Debug.Print Split(strVal, ".")(0) & "." & Left(Split(strVal, ".")(1), 2)
Else
Debug.Print dVal
End If
You can use Int() function. Debug.print Int(1.99543)
Or Better:
Public Function Trunc(ByVal value As Double, ByVal num As Integer) As Double
Trunc = Int(value * (10 ^ num)) / (10 ^ num)
End Function
So you can use Trunc(1.99543, 4) ==> result: 1.9954
This was my attempt:
Function TruncateNumber(decimalNum As Double, decPlaces As Integer) As Double
'decimalNum: the input number to be truncated
'decPlaces: how many decimal places to round to. Use 0 for no decimal places.
decimalLocation = InStr(decimalNum, ".")
TruncateNumber = Left(decimalNum, decimalLocation + decPlaces)
End Function
It uses strings to avoid any math errors caused by different rounding methods. It will output as a type double, so you can still perform your own math on it.
This will cause an error if a number without a decimal place is passed into the above function. If this is a concern, you can use the following code instead:
Function TruncateNumber(decimalNum As Double, decPlaces As Integer) As Double
'decimalNum: the input number to be truncated
'decPlaces: how many decimal places to round to. Use 0 for no decimal places.
If InStr(decimalNum, ".") = 0 Then 'if there was no decimal:
'then return the number that was given
TruncateNumber = decimalNum
Else 'if there is a decimal:
'then return the truncated value as a type double
decimalLocation = InStr(decimalNum, ".")
TruncateNumber = Left(decimalNum, decimalLocation + decPlaces)
End If
End Function
Hopefully these functions are of some use to someone. I haven't done extensive testing, but they worked for me.
EDITED
Newer version of Excel (VBA) have a TRUNC function which already does things properly.
For older versions of EXCEL
I wanted to truncate a double into an integer.
value = Int(83.768)
value == 83
Awesome, it worked.
Depending on your version of Excel (VB) this might not work with negative numbers.
value = Int(-83.768)
value == -84
VB uses Banker rounding.
Public Function Trunc1(ByVal value As Double) As Integer
' Truncate by calling Int on the Absolute value then multiply by the sign of the value.
' Int cannot truncate doubles that are negative
Trunc1 = Sgn(value) * Int(Abs(value))
End Function
If you want specific decimal places do what Makah did only with Abs around the value so Int can truncate properly.
Public Function Trunc2(ByVal value As Double, Optional ByVal num As Integer = 1) As Double
' Truncate by calling Int on the Absolute value then multiply by the sign of the value.
' Int cannot truncate doubles that are negative
Trunc2 = Sgn(value) * (Int(Abs(value) * (10 ^ num)) / (10 ^ num))
End Function
Here is a little experiment I did... (1st time posting and answer, please tell me if I am not following conventions.
Sub Truncate()
Dim dblNum As Double
Dim intDecimal As Integer
dblNum = 1578.56789
intDecimal = 2 '0 returns 1578
'2 returns 1578.56
'-2 returns 1500
Debug.Print (Int(dblNum * 10 ^ intDecimal) / 10 ^ intDecimal)
End Sub

Resources