How do I convert VB's Double to COBOL's COMP-3? - vb.net

Does anyone here know how to convert a VB Double to Cobol S9(15)V99 Comp-3 data type?

If it doesn't need to be done in the same program, it seems to me it would be easier to find a common format that both VB and COBOL can understand.
That would be text. In other words, the simplest solution may be to write the number out to a file as text "3.14159" and have the COBOL code read it in in that format and MOVE it to the COMP-3 field?
If that's not possible, the COMP-3 is a fairly simple BCD type. I would convert the number to a string anyway then take it two characters at a time into a byte array.
The S9(15)V99 requires 18 nybbles (a nybble being 4 bits, or half an octet) to store:
the integer bit (fifteen nybbles).
the fractional bit (two nybbles).
the sign (one nybble).
No space is needed for the decimal point since a V is an implied decimal, not a real one.
So the number 3.14 would be represented as the bytes:
00 00 00 00 00 00 00 31 4C
The only tricky bit is that final sign nybble (C for positive and D for negative).
Here's a bit of code I whipped up in Excel VBA (I don't have VB installed on this machine unfortunately) that shows you how to do it. The makeComp3() function should be easily transferred into a real VB program.
The macro test program outputs the values 0, 49 and 76 which are hex 00, 31 and 4C respectively (00314C is +3.14).
The first step (after all the declarations) is to make the double an implied decimal by multiplying it by the relevant power of ten then turning it into an integer:
Option Explicit
' makeComp3. '
' inp is the double to convert. '
' sz is the minimum final size (with sign). '
' frac is the number of fractional places. '
Function makeComp3(inp As Double, sz As Integer, frac As Integer) As String
Dim inpshifted As Double
Dim outstr As String
Dim outbcd As String
Dim i As Integer
Dim outval As Integer
Dim zero As Integer
zero = Asc("0")
' Make implied decimal. '
inpshifted = Abs(inp)
While frac > 0
inpshifted = inpshifted * 10
frac = frac - 1
Wend
inpshifted = Int(inpshifted)
Next, we make it into a string of the correct size, to make processing easier:
' Get as string and expand to correct size. '
outstr = CStr(inpshifted)
While Len(outstr) < sz - 1
outstr = "0" & outstr
Wend
If Len(outstr) Mod 2 = 0 Then
outstr = "0" & outstr
End If
Then we process that string two digits at a time and each pair is combined into an output nybble. The final step is to process the last digit along with the sign:
' Process each nybble pair bar the last. '
outbcd = ""
For i = 1 To Len(outstr) - 2 Step 2
outval = (Asc(Mid(outstr, i)) - zero) * 16
outval = outval + Asc(Mid(outstr, i + 1)) - zero
outbcd = outbcd & Chr(outval)
Next i
' Process final nybble including the sign. '
outval = (Asc(Right(outstr, 1)) - zero) * 16 + 12
If inp < 0 Then
outval = outval + 1
End If
makeComp3 = outbcd & Chr(outval)
End Function
And this is just the test harness, though it could probably do with a few more test cases :-)
Sub Macro1()
Dim i As Integer
Dim cobol As String
cobol = makeComp3(3.14159, 6, 2)
For i = 1 To Len(cobol)
MsgBox CStr(Asc(Mid(cobol, i)))
Next i
End Sub

Related

vba get random with string as seed

I'm working under MS-Visio 2010 in VBA (not an expert) and I want to generate a random number (several numbers would be even better) based on a string as seed.
I know that Rnd(seed) with seed as a negative number exists. However, I don't know about any random generator with a string as seed. Maybe some kind of hash function with a number as result ?
I'd like something like :
print function("abc")
45
print function("xyz abc-5")
86
print function("abc")
45
with spaces, symbols and numbers support when inside the seed string.
I may see a workaround by converting each character to some ascii number corresponding and somehow using this big number as seed with Rnd but it definitely feels far-fetched. Does anyone knows of a fancier way of doing so ?
Combined these examples
VBA hash string
Convert HEX string to Unsigned INT (VBA)
to:
Function hash4(txt)
' copied from the example
Dim x As Long
Dim mask, i, j, nC, crc As Integer
Dim c As String
crc = &HFFFF
For nC = 1 To Len(txt)
j = Asc(Mid(txt, nC)) ' <<<<<<< new line of code - makes all the difference
' instead of j = Val("&H" + Mid(txt, nC, 2))
crc = crc Xor j
For j = 1 To 8
mask = 0
If crc / 2 <> Int(crc / 2) Then mask = &HA001
crc = Int(crc / 2) And &H7FFF: crc = crc Xor mask
Next j
Next nC
c = Hex$(crc)
' <<<<< new section: make sure returned string is always 4 characters long >>>>>
' pad to always have length 4:
While Len(c) < 4
c = "0" & c
Wend
Dim Hex2Dbl As Double
Hex2Dbl = CDbl("&h0" & c) ' Overflow Error if more than 2 ^ 64
If Hex2Dbl < 0 Then Hex2Dbl = Hex2Dbl + 4294967296# ' 16 ^ 8 = 4294967296
hash4 = Hex2Dbl
End Function
Try in immediate (Ctrl + G in VBA editor window):
?hash4("Value 1")
31335
?hash4("Value 2")
31527
This function will:
return different number for different input strings
sometimes they will match, it is called hash-collisions
if it is critical, you can use md5, sha-1 hashes, their examples in VBA also available
return same number for same input strings

Convert 32-bit signed integer to 64-bit integer while preserving the exact bits

I have a 32-bit value that is stored in the VB.Net type Integer (i.e. Int32.) I am only interested in the bits - not the numerical value. Sometimes the 32nd bit is a one which is interpreted as a negative number. My goal is to reverse the actual bits. My original data is encoded into bits right-to-left (LSB right-most) and is read back in left-to-right (MSB left-most.) I am adapting someone else's code and design. One thought I had was maybe to convert to a long temporarily but I don't know how to do that and preserve the 32nd bit correctly.
Public Shared Function ReverseBits32(ByVal n As Integer) As Integer
Dim result As Integer = 0
For i As Integer = 0 To 32 - 1
result = result * 2 + n Mod 2
n = n >> 1 'n Or 2
Next
Return result
End Function
If you had a method to reverse the bits of a byte you could apply it four times to the bytes of an integer. A little research finds Bit Twiddling Hacks.
Module Module1
Sub ShowBits(a As Integer)
Dim aa = BitConverter.GetBytes(a)
Console.WriteLine(String.Join(" ", aa.Select(Function(b) Convert.ToString(b, 2).PadLeft(8, "0"c))))
End Sub
Function ReverseBits(b As Byte) As Byte
' From https://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits
Dim c = CULng(b)
Return CByte((((c * &H802UL And &H22110UL) Or (c * &H8020UL And &H88440UL)) * &H10101UL >> 16) And &HFFUL)
End Function
Function ReverseBits(a As Integer) As Integer
Dim bb = BitConverter.GetBytes(a)
Dim cc(3) As Byte
For i = 0 To 3
cc(3 - i) = ReverseBits(bb(i))
Next
Return BitConverter.ToInt32(cc, 0)
End Function
Sub Main()
Dim y = -762334566
ShowBits(y)
y = ReverseBits(y)
ShowBits(y)
Console.ReadLine()
End Sub
End Module
Output from test value:
10011010 10110010 10001111 11010010
01001011 11110001 01001101 01011001
I used the "no 64-bit" method because it is written for a language where arithmetic overflow is ignored - the methods using 64-bit operations rely on that but it is not the default for VB.NET.

7 Byte Hex to Dec Conversion

I need to convert 7 byte hexadecimal values in an Excel column to the decimal equivalents in an adjacent column.
I have over 2000 unique values to convert from hexadecimal to decimal.
I got as far as using Excel's hex2bin and then bin2dec formulas.
I found that Excel is rounding up the least significant 4 decimal places.
Example:
7Byte Hex: 0x803277323A8904
Excel Output: 36084284544158000
Correct Decimal Number: 36084284544157956
This is a small variation of Rick Rothstein's code
Function HexToDecs(ByVal HexString As String) As String
Dim X As Integer
Dim BinStr As String
Const BinValues = "0000000100100011010001010110011110001001101010111100110111101111"
If Left$(HexString, 2) Like "&[hH]" Then
HexString = Mid$(HexString, 3)
End If
If Len(HexString) <= 23 Then
For X = 1 To Len(HexString)
BinStr = BinStr & Mid$(BinValues, 4 * Val("&h" & Mid$(HexString, X, 1)) + 1, 4)
Next
HexToDecd = CDec(0)
For X = 0 To Len(BinStr) - 1
HexToDecd = HexToDecd + Val(Mid(BinStr, Len(BinStr) - X, 1)) * 2 ^ X
Next
Else
' Number is too big, handle error here
End If
HexToDecs = CStr(HexToDecd)
End Function
NOTE:
This UDF() returns a String representation of the integer to avoid the 15 digit limitation to true numeric values.
I have elected not to start my input string with 0x
Excel maximum number of digits. In Excel spreadsheet, there is a limit for storing a number in a Cell, which is 15 digits (15 numbers) regardless of whether the numbers are decimal places. Excel call this “15 significant digits of precision” which adheres to “IEEE 754”.Feb 24, 2015
In order to have 36084284544157956, which has 17 digits, save the cell as a Text.
Even VBA does not like displaying such big numbers:
Public Sub TestMe()
Dim inputString As String: inputString = "123456789012345678"
Dim someValue As Double
someValue = inputString
Debug.Print someValue + 1
End Sub
gets: 1,23456789012346E+17
To present the text value in Excel cell, make sure that you format the cell before putting the text in it:
Option Explicit
Public Sub TestMe()
With Range("D2")
.NumberFormat = "#"
.Value2 = "123456789012345678"
End With
End Sub

signed result of Val function in VBA

I use vba in ms access,and found that ,if my parameter greater than 0x8000
less than 0x10000, the result is minus number
eg. Val("&H8000") = -32768 Val("&HFFFF")= -1
how can i get the unsigned number?
thanks!
There's a problem right here:
?TypeName(&HFFFF)
Integer
The Integer type is 16-bit, and 65,535 overflows it. This is probably overkill, but it was fun to write:
Function ConvertHex(ByVal value As String) As Double
If Left(value, 2) = "&H" Then
value = Right(value, Len(value) - 2)
End If
Dim result As Double
Dim i As Integer, j As Integer
For i = Len(value) To 1 Step -1
Dim digit As String
digit = Mid$(value, i, 1)
result = result + (16 ^ j) * Val("&H" & digit)
j = j + 1
Next
ConvertHex = result
End Function
This function iterates each digit starting from the right, computing its value and adding it to the result as it moves to the leftmost digit.
?ConvertHex("&H10")
16
?ConvertHex("&HFF")
255
?ConvertHex("&HFFFF")
65535
?ConvertHex("&HFFFFFFFFFFFFF")
281474976710655
UPDATE
As I suspected, there's a much better way to do this. Credits to #Jonbot for this one:
Function ConvertHex(ByVal value As String) As Currency
Dim result As Currency
result = CCur(value)
If result < 0 Then
'Add two times Int32.MaxValue and another 2 for the overflow
'Because the hex value is apparently parsed as a signed Int64/Int32
result = result + &H7FFFFFFF + &H7FFFFFFF + 2
End If
ConvertHex = result
End Function
Append an ampersand to the hex literal to force conversion to a 32bit integer:
Val("&HFFFF" & "&") == 65535
Val("&H8000&") == +32768
Don't use Val. Use one of the built-in conversion functions instead:
?CCur("&HFFFF")
65535
?CDbl("&HFFFF")
65535
Prefer Currency over Double in this case, to avoid floating-point issues.

VB6 comparing only numeric characters in srings

I need to compare phone numbers from a CSV file to phone numbers in an SSMS database in VB6 without using the .Net Library. One may have a number as 555-555-5555 and the other may have the same number as (555) 555-5555 which obviously kicks back as different when strings are compared.
I know I can use for loops and a buffer to pull out only numeric characters like:
Public Function PhoneNumberNumeric(PhoneNumberCSV As String) As String
Dim CharNdx As Integer
Dim buffer As String
For CharNdx = 1 To Len(PhoneNumberCSV) Step 1
If IsNumeric(Mid(PhoneNumberCSV, CharNdx, 1)) Then
buffer = buffer + Mid(PhoneNumberCSV, CharNdx, 1)
End If
Next
PhoneNumberNumeric = buffer
End Function
but this is expensive. Is there a less expensive way to do this?
This should be a bit quicker:
Private Function Clean(ByRef Original As String) As String
Dim I As Long
Dim J As Long
Dim Char As Long
Clean = Space$(10)
For I = 1 To Len(Original)
Char = AscW(Mid$(Original, I, 1))
If 48 <= Char And Char <= 57 Then
J = J + 1
If J > 10 Then Exit For 'Or raise an exception.
Mid$(Clean, J, 1) = ChrW$(Char)
End If
Next
End Function
It avoids string concatenation, ANSI conversions, and VBScript-form "pigeon VB" (use of slow Variant functions).