using a long integer in in snprintf will not go over 65k - printf

I am trying to format and display zero padded long integers with the snprintf command
long position = 0; // defined elsewhere in the program
long micronPerStep = 5;
long adjustedPosition = (position / micronsPerStep) * 25;
char txt[17];
int n = snprintf(txt,16,"%09u ",adjustedPosition);
The adjustedPosition will range from -1 to 99999999. I have tried d and u, but the number goes negative at about 65,xxx. What am I doing wrong???

%u is the format code for printing unsigned int.
If you want to print unsigned long, you need %lu.

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

Long HEX Value Split to 2bytes Hex values or 4bytes Hex Values and convert it to decimal (float) values Using VB.Net

I want to split my long array to 2bytes (4 digit HEX) or 4bytes(8 digit HEX). If c value is 1, I want to get 2bytes (4 digit HEX) HEX array and if c value is 0, I want to get 4bytes(8 digit HEX) HEX array from a long HEX string.
After that, I want to convert while converting 2byte or 4byte to floating number( decimal number).
I have code for 4byte to a decimal value.
Dim bytes() As Byte = {&H43, &H62, &HD3, &HE}
If BitConverter.IsLittleEndian Then
Array.Reverse(bytes)
End If
Dim myFloat As Decimal = BitConverter.ToSingle(bytes, 0)
txt4.Text = myFloat
Please provide me the code for this function.
Example:
Long Hex value - 4362D30EFFC00000FFC00000FFFFFFFF
If C is 1, split 4 digit HEX values.
4362
Then convert to decimal.
17250
Again split 4 digit HEX values.
D30E
Then convert to decimal
-11506
If c is 0, split 8 digit HEX values.
4362D30E
Then convert to decimal
226.824432
Please help with this. I don't not know much about VB.Net
I'm going to give this a shot as well, but do consider what I said on your newer question.
To read a certain amount of bytes and turn them into a number best is to use the BitConverter and its To*** methods.
This is how many bytes each numeric data type uses:
Double: 8 bytes
Int16 / UInt16 / Short / UShort: 2 bytes
Int32 / UInt32 / Integer / UInteger: 4 bytes
Int64 / UInt64 / Long / ULong: 8 bytes
Single: 4 bytes
With this in mind, all you need is to decide on a data type based on the number of bytes you want to read, and then specify an index in your array where to start reading from.
Remember that if you reverse your array you have to start reading from the end of the array instead.
Dim StartIndex As Integer = 0
Dim EndIndex As Integer = bytes.Length - 1
'How many bytes to read: 2 or 4.
Dim BytesPerStep As Integer = If(C = 1, 2, 4)
'If the system uses little endianness we need to reverse the array and loop backwards.
If BitConverter.IsLittleEndian Then
'Reverse the array.
Array.Reverse(bytes)
'Swap start and end index.
StartIndex = EndIndex
EndIndex = 0
'Negate BytesPerStep: Go backwards.
BytesPerStep = -BytesPerStep
End If
'Iterate the array <BytesPerStep> bytes at a time.
For i = StartIndex To EndIndex Step BytesPerStep
If C = 1 Then
'Read two bytes = Short (Int16).
ListBox1.Items.Add(BitConverter.ToInt16(bytes, i))
Else
'Read four bytes = Single (Float)
ListBox1.Items.Add(BitConverter.ToSingle(bytes, i))
End If
Next

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.

Set Long Variable in VBA

Simple question that I can't find anywhere. When I'm doing arithmetic, it seems the natural type is to treat value as 16Bit Integers.
I'm trying to save the result of 60 * 60 * 8 * 5 into a long, but I get an Overflow error before it even has a chance to save the number as a Long:
Dim secondsInAWorkWeek As Long
secondsInAWorkWeek = 60 * 60 * 8 * 5
Long should happily store anything up to 231 = 2,147,483,647
How can I perform the multiplication safely to convert into a long
There are certain conventions in order to force a literal to a specific type from a generic (default) type: http://msdn.microsoft.com/en-us/library/dzy06xhf.aspx
Which leads to this code:
Dim secondsInAWorkWeek As Long
Let secondsInAWorkWeek = 60& * 60& * 8& * 5&
or:
Const DAYS_IN_WEEK = 5&
Const HOURS_IN_DAY = 8&
Const MINUTES_IN_HOUR = 60&
Const SECONDS_IN_MINUTE = 60&
Dim secondsInAWorkWeek As Long
Let secondsInAWorkWeek = _
DAYS_IN_WEEK _
* HOURS_IN_DAY _
* MINUTES_IN_HOUR _
* SECONDS_IN_MINUTE
A lot more to write, true, but is type safe, barely needs any more explanations/comments, and it will be easy to change when they'll vote for 10 hours workdays. :-)
Ok, according to Overflow when multiplying Integers and assigning to Long, you need to give Excel a running start, otherwise it will default each value in the multiplication to an integer.
Just start off by casting to long with CLng and it will take care of the rest:
Dim secondsInAWorkWeek As Long
secondsInAWorkWeek = CLng(1) * 60 * 60 * 8 * 5

Mod with Doubles

Am I doing something wrong or does the VBA Mod operator actually not work with floating point values like Doubles?
So I've always sort of assumed that the VBA Mod operator would work with Doubles based on the VB documentation, but in trying to figure out why my rounding function doesn't work, I found some unexpected Mod behavior.
Here is my code:
Public Function RoundUp(num As Double, Optional nearest As Double = 1)
RoundUp = ((num \ nearest) - ((num Mod nearest) > 0)) * nearest
End Function
RoundUp(12.34) returns 12 instead of 13 so I dug a little deeper and found that:
12.5 Mod 1 returns 0 with the return type of Long, whereas I had expected 0.5 with a type of Double.
Conclusion
As #ckuhn203 points out in his answer, according to the VBA specification,
The modulus, or remainder, operator divides number1 by number2
(rounding floating-point numbers to integers) and returns only the
remainder as result.
And
Usually, the data type of result is a Byte, Byte variant, Integer,
Integer variant, Long, or Variant containing a Long, regardless of
whether or not result is a whole number. Any fractional portion is
truncated.
For my purposes, I need a floating point modulo and so I have decided to use the following:
Public Function FMod(a As Double, b As Double) As Double
FMod = a - Fix(a / b) * b
'http://en.wikipedia.org/wiki/Machine_epsilon
'Unfortunately, this function can only be accurate when `a / b` is outside [-2.22E-16,+2.22E-16]
'Without this correction, FMod(.66, .06) = 5.55111512312578E-17 when it should be 0
If FMod >= -2 ^ -52 And FMod <= 2 ^ -52 Then '+/- 2.22E-16
FMod = 0
End If
End Function
Here are some examples:
FMod(12.5, 1) = 0.5
FMod(5.3, 2) = 1.3
FMod(18.5, 4.2) = 1.7
Using this in my rounding function solves my particular issue.
According to the VB6/VBA documentation
The modulus, or remainder, operator divides number1 by number2
(rounding floating-point numbers to integers) and returns only the
remainder as result. For example, in the following expression, A
(result) equals 5. A = 19 Mod 6.7 Usually, the data type of result is
a Byte, Byte variant, Integer, Integer variant, Long, or Variant
containing a Long, regardless of whether or not result is a whole
number. Any fractional portion is truncated. However, if any
expression is Null, result is Null. Any expression that is Empty is
treated as 0.
Remember, mod returns the remainder of the division. Any integer mod 1 = 0.
debug.print 12 mod 1
'12/1 = 12 r 0
The real culprit here though is that vba truncates (rounds down) the double to an integer before performing the modulo.
?13 mod 10
'==>3
?12.5 mod 10
'==>2
debug.print 12.5 mod 1
'vba truncates 12.5 to 12
debug.print 12 mod 1
'==> 0
I believe that the Mod operator calculates with long type only. The link that you provided is for VB.Net, which is not the same as the VBA you use in MSAccess.
The operator in VBA appears to accept a double type, but simply converts it to a long internally.
This test yielded a result of 1.
9 Mod 4.5
This test yielded a result of 0.
8 Mod 4.5
As a work around your can do some simple math on the values. To get two decimal of precision just multiply the input values by 100 and then divide the result by 100.
result = (123.45*100 Mod 1*100)/100
result = (12345 Mod 100)/100
result = 0.45
I'm late to the party, but just incase this answer is still helpful to someone.
Try This in VBS:
Option Explicit
Call Main()
Sub Main()
WScript.Echo CStr(Is_Rest_Of_Divide_Equal_To_Zero(506.25, 1.5625))
End Sub
Function Is_Rest_Of_Divide_Equal_To_Zero(Divident, Divisor)
Dim Result
Dim DivideResult
If Divident > Divisor Then
DivideResult = Round(Divident/Divisor, 0)
If (DivideResult * Divisor) > Divident Then
Result = False
ElseIf (DivideResult * Divisor) = Divident Then
Result = True
ElseIf (DivideResult * Divisor) < Divident Then
Result = False
End If
ElseIf Divident = Divisor Then
Result = True
ElseIf Divident < Divisor Then
Result = False
End If
Is_Rest_Of_Divide_Equal_To_Zero = Result
End Function
Public Function Modi(d as double) as double
Modi = d - Int(d)
End Function
Dim myDoule as Double
myDoule = 1.99999
Debug.Print Modi(myDoule)
0.99999