Results 1 to 14 of 14

Thread: LongLong

  1. #1

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,909

    LongLong

    Please see post #6. I found an easier way of doing this without needing to use Decimals.

    Hi All,

    I was inspired by a post by hwoarang over in the codebank to write a class (and a supporting bas) that I might actually use.

    It's a class that provides the VBA LongLong type to VB6. Now, before everyone jumps on it, it's not "native" 8-byte integers, but it gets it all done as if it were. I'm sure it's slower than native 8-byte integers would be.

    However, the only initial difference I can see from what I'm doing and what VBA does, is that I require the use of the "New" keyword when declaring variables. Here's some test code in a form:

    Code:
    
    Option Explicit
    
    
    Private Sub Form_Load()
        Dim LL1 As New LongLong
        Dim LL2 As New LongLong
        Dim LL3 As New LongLong
    
    
        '
        ' Some simple tests.
        '
        LL1 = 5
        LL2 = LL1
    
        MsgBox LL1
        MsgBox LL2
    
        MsgBox LL1 + LL2
        MsgBox LL1 * LL2
        MsgBox LL1 / LL2
        MsgBox LL1 ^ LL2
        LL3 = LL1 ^ LL2
        MsgBox LL3
    
    
    
        '
        ' Some more rigorous tests.
        '
        LL1 = CLngLng("9223372036854775807") ' Largest LongLong value in VBA.
        MsgBox LL1
        'LL1 = LL1 + 1   ' This will be an overflow.
    
        LL1 = CLngLng("-9223372036854775807") ' (Almost) smallest LongLong value in VBA.
        MsgBox LL1
        LL1 = LL1 - 1   ' This not overflow.
        MsgBox LL1
        'LL1 = LL1 - 1   ' This will be an overflow.
    
        Dim d As Double
        d = 9.22337203685477E+18
        LL1 = d
        MsgBox LL1
        'LL1 = LL1 * 2 ' This will overflow.
    
    End Sub
    
    As you can see, the usage is VERY close to the way you'd use VBA LongLongs.

    I've attached a sample project which includes the Class that gets it done, along with a supporting BAS module. In the BAS module, there's a CLngLng function that'll convert about anything to a Decimal (with overflow checks at LongLong ranges). This can be directly placed into a LongLong declared variable (as those are actually handed back as Decimals also so that math natively works).

    I'm open to whatever critiques/bugs people would like to make/report about any of this. Maybe after I've polished it some more, I'll post it in the codebank.

    Regards,
    Elroy
    Attached Files Attached Files
    Last edited by Elroy; Dec 2nd, 2016 at 02:39 PM.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  2. #2
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,598

    Re: LongLong

    Quote Originally Posted by Elroy View Post
    ...
    It's a class that provides the VBA LongLong type to VB6. Now, before everyone jumps on it, it's not "native" 16-byte integers, but it gets it all done as if it were. I'm sure it's slower than native 16-byte integers would be.

    ...

    LL1 = CLngLng("9223372036854775807") ' Largest LongLong value in VBA.

    ...
    "9223372036854775807" is 2^63-1, which would be a signed 8-byte value, not 16 bytes.

  3. #3

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,909

    Re: LongLong

    Ahhh, you're right. Sorry about that. Yeah, a LongLong is 8-bytes. I got carried away. I'll fix the comments.

    EDIT1: The code is all fine. It was just my mistake in the verbiage of the OP.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  4. #4
    The Idiot
    Join Date
    Dec 2014
    Posts
    3,002

    Re: LongLong

    why not just use variant? you can assign large numbers with it.
    the only different is the conversion of 9.22337203685477E+18, while your code will "round it" variant will just copy it as it is.

  5. #5

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,909

    Re: LongLong

    @ baka: Yeah, the Decimal type of a Variant does work quite well. There's a bit of a problem of getting initial values into them, but I've also got that worked out. I was just trying to see how close I could get to emulating the exact behavior of a VBA LongLong. The more I think about it, I'm not sure if I'd ever really use it or not. In VB6, Longs give me high enough integers that I doubt I'd ever overflow them. And, even in VBA, I don't think the LongLong types are used for much other than hanging onto memory pointers. It's probably just an academic exercise.

    I've also already found a place where my methods don't exactly emulate the VBA LongLong, the use of the binary operators (And, Or, etc). They work with Decimals, but Decimals aren't stored as Twos-Compliment numbers, whereas all other integers are stored as Twos-Compliment. Therefore, anytime we're dealing with negative numbers, the bit patterns will be different.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  6. #6

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,909

    Re: LongLong

    Okay, I've discovered that there's a much easier way to do this. To my surprise, the LongLong type is actually available to VB6. This may be dependent on the version of oleaut32.dll you have, but I'm on Win7-64, and it's working fine.

    If anyone would want to test on an old true 32-bit machine, I'd be curious to see if this would all work.

    Here's my latest LongLong class (posted in full, so use Notepad to save the module).

    Code:
    VERSION 1.0 CLASS
    BEGIN
      MultiUse = -1  'True
      Persistable = 0  'NotPersistable
      DataBindingBehavior = 0  'vbNone
      DataSourceBehavior  = 0  'vbNone
      MTSTransactionMode  = 0  'NotAnMTSObject
    END
    Attribute VB_Name = "LongLong"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = True
    Attribute VB_PredeclaredId = False
    Attribute VB_Exposed = False
    '
    ' A class that allows access to the LongLong type (signed).
    '
    ' +, -, *       These all work as expected.
    ' /             Converts to a Double, with possible loss of precision.
    ' \, Mod        Works as expected.
    ' ^             Converts to a Double, with possible loss of precision.
    ' Int()         Works as expected.
    ' Fix()         Works as expected.
    ' Abs()         Works as expected.
    ' And, Or, etc  Works as expected.
    ' Sgn()         Works as expected, although return is an Integer.
    ' Sqr(), etc.   Most of these functions (including trig) return Double, not Decimal, so some precision may be lost.
    '
    Option Explicit
    '
    ' https://msdn.microsoft.com/en-us/library/cc237865.aspx for all the types.
    Private Declare Function VariantChangeTypeEx Lib "oleaut32" (ByRef pvargDest As Variant, ByRef pvarSrc As Variant, ByVal lcid As Long, ByVal wFlags As Integer, ByVal vt As Integer) As Long
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Dst As Any, Src As Any, ByVal numbytes As Long)
    '
    Private Const VT_I8 As Integer = &H14
    Private Const VT_ERROR As Integer = &HA
    Private Const LOCALE_INVARIANT As Long = &H7F&
    '
    Private Const LLOverflow = "Overflow - In LongLong procedures"
    Private Const LLTypeMismatch = "Type mismatch - In LongLong procedures"
    Private Const LLApiTypeError = "VariantChangeTypeEx error - In LongLong procedures"
    Private Const LLStrConst = "LongLong"
    '
    Dim mllValue As Variant  ' THIS is the "master" value.
    '
    
    Friend Function Ptr() As Long
        Ptr = VarPtr(mllValue) + 8 ' This points directly at the LongLong, and not the variant.
    End Function
    
    Public Property Get Value() As Variant
    Attribute Value.VB_UserMemId = 0
    Attribute Value.VB_MemberFlags = "200"
        Value = mllValue
    End Property
    
    Public Property Let Value(v As Variant)
        '
        ' Warning about strings with HEX in them.
        ' In "almost" all cases, using this method will work absolutely fine for these LongLong types.
        ' However, if your HEX string is 10 characters (8 characters of HEX, eg. "&Hffffffff"),
        ' and the Long (not LongLong) sign bit is on, the number will be interpreted as negative.
        ' To prevent this, do assignment with HexVal property.
        '
        Dim vt As Integer
        ' Make sure it's a type we can deal with.
        vt = VarType(v)
        If (vt = vbArray) Or _
           (vt = vbObject) Or _
           (vt = vbUserDefinedType) Or _
           (vt = vbEmpty) Or _
           (vt = vbNull) Or _
           (vt = vbError) Or _
           (vt = vbVariant) Or _
           (vt = vbDataObject) Then
                Err.Raise 13, LLStrConst, LLTypeMismatch
                Exit Property
        End If
        '
        If VarType(mllValue) <> VT_I8 Then SetToLongLongZero ' This should happen only if there's been an error.
        mllValue = v
        If VarType(mllValue) <> VT_I8 Then
            mllValue = Format$(mllValue) ' This helps with overflow, especially when it's a large Double.
            CheckForErrors VariantChangeTypeEx(mllValue, mllValue, LOCALE_INVARIANT, 0, VT_I8)
        End If
    End Property
    
    Friend Property Get HexVal() As String
        Dim lLo As Long
        Dim lHi As Long
        '
        If VarType(mllValue) <> VT_I8 Then Exit Sub ' This should happen only if there's been an error.
        '
        CopyMemory lHi, ByVal VarPtr(mllValue) + 12, 4
        CopyMemory lLo, ByVal VarPtr(mllValue) + 8, 4
        '
        If lHi = 0 Then
            HexVal = Hex$(lLo)
        Else
            HexVal = Hex$(lHi) & Right$("0000000" & Hex$(lLo), 8)
        End If
    End Property
    
    Friend Property Let HexVal(s As String)
        If UCase$(Left$(s, 2)) <> "&H" Then
            Err.Raise 13, LLStrConst, LLTypeMismatch
            Exit Property
        End If
        If Len(s) <> 10 Then
            mllValue = s
            CheckForErrors VariantChangeTypeEx(mllValue, mllValue, LOCALE_INVARIANT, 0, VT_I8)
        Else    ' We'll just go around the Long sign bit.
            mllValue = s & "0"
            CheckForErrors VariantChangeTypeEx(mllValue, mllValue, LOCALE_INVARIANT, 0, VT_I8)
            mllValue = mllValue \ &H10
        End If
    End Property
    
    Friend Property Get CurrNoDec() As Currency
        ' This one lets you return a Currency while ignoring the implicit 4th decimal place.
        If VarType(mllValue) <> VT_I8 Then SetToLongLongZero ' This should happen only if there's been an error.
        CopyMemory CurrNoDec, ByVal VarPtr(mllValue) + 8, 8
    End Property
    
    Friend Property Let CurrNoDec(c As Currency)
        ' This one lets you assign a Currency while ignoring the implicit 4th decimal place.
        CopyMemory mllValue, VT_I8, 2
        CopyMemory ByVal VarPtr(mllValue) + 8, c, 8
    End Property
    
    '*****************************************************
    '*****************************************************
    ' Everything is private from here down.
    '*****************************************************
    '*****************************************************
    '
    Private Sub Class_Initialize()
        SetToLongLongZero
    End Sub
    
    Private Sub SetToLongLongZero()
        mllValue = 0&
        VariantChangeTypeEx mllValue, mllValue, LOCALE_INVARIANT, 0, VT_I8
    End Sub
    
    Private Sub CheckForErrors(lRet As Long)
        Const S_OK = 0&
        Const DISP_E_BADVARTYPE = &H80020008
        Const DISP_E_OVERFLOW = &H8002000A
        Const DISP_E_TYPEMISMATCH = &H80020005
        Const E_INVALIDARG = &H80070057
        Const E_OUTOFMEMORY = &H8007000E
        '
        Select Case lRet
        Case S_OK
            ' Nothing to do here, all good.
        Case DISP_E_BADVARTYPE
            Err.Raise lRet, LLStrConst, LLApiTypeError
        Case DISP_E_OVERFLOW
            Err.Raise 6, LLStrConst, LLOverflow
        Case DISP_E_TYPEMISMATCH
            Err.Raise lRet, LLStrConst, LLApiTypeError
        Case E_INVALIDARG
            Err.Raise lRet, LLStrConst, LLApiTypeError
        Case E_OUTOFMEMORY
            Err.Raise lRet, LLStrConst, LLApiTypeError
        End Select
    End Sub
    And here's a bit of test code to throw into a Form1 (just paste this one into Form1 in IDE):

    Code:
    
    Option Explicit
    
    Private Type WIN32_FIND_DATA
       dwFileAttributes As Long
       ftCreationTime   As Currency
       ftLastAccessTime As Currency
       ftLastWriteTime  As Currency
       nFileSize        As Currency
       dwReserved       As Currency
       cFileName        As String * 260
       cAlternate       As String * 14
    End Type
    '
    Private Type SYSTEMTIME
       wYear            As Integer
       wMonth           As Integer
       wDayOfWeek       As Integer
       wDay             As Integer
       wHour            As Integer
       wMinute          As Integer
       wSecond          As Integer
       wMilliseconds    As Integer
    End Type
    '
    Private Declare Function FindFirstFile Lib "kernel32" Alias "FindFirstFileA" (ByVal lpFileName As String, lpFindFileData As WIN32_FIND_DATA) As Long
    Private Declare Function FindClose Lib "kernel32" (ByVal hFindFile As Long) As Long
    Private Declare Function FileTimeToSystemTimePtr Lib "kernel32" Alias "FileTimeToSystemTime" (lpFileTimePtr As Long, lpSystemTime As SYSTEMTIME) As Long
    Private Declare Function FileTimeToSystemTime Lib "kernel32" (lpFileTime As Currency, lpSystemTime As SYSTEMTIME) As Long
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Dst As Any, Src As Any, ByVal numbytes As Long)
    '
    
    Private Sub Form_Load()
        Dim LL1 As New LongLong
        Dim LL2 As New LongLong
        Dim LL3 As New LongLong
    
    
        Const sLargeFileSpec = "C:\Users\Elroy\Desktop\test.txt"
    
    
    
        '
        ' Some simple tests.
        '
        LL1 = 5
        LL2 = LL1 And 4
        MsgBox LL2
        MsgBox LL1 + LL2
        MsgBox LL1 * LL2
        MsgBox LL1 / LL2 ' This will result in a Double.
        MsgBox LL1 \ LL2
        MsgBox LL1 ^ LL2
        LL3 = LL1 ^ LL2
        MsgBox LL3
    
        LL1.HexVal = "&Hffffffff8"
        MsgBox LL1
        MsgBox LL1.HexVal
    
    
    
    
        '
        ' Some more rigorous tests.
        '
        LL1 = "9223372036854775807" ' Largest LongLong value in VBA.
        MsgBox LL1
        'LL1 = LL1 + 1   ' This will be an overflow.
    
        LL1 = "-9223372036854775807" ' Smallest LongLong value in VBA.
        MsgBox LL1
        LL1 = LL1 - 1   ' This not overflow.
        MsgBox LL1
        'LL1 = LL1 - 1   ' This will be an overflow.
    
        Dim d As Double
        d = 9.22337203685477E+18
        LL1 = d
        MsgBox LL1
        'LL1 = LL1 * 2 ' This will overflow.
    
    
    
        '
        ' Test the use of the pointer with API calls.
        '
        Dim hSearch As Long
        Dim wfd As WIN32_FIND_DATA
    
        hSearch = FindFirstFile(sLargeFileSpec, wfd)
        FindClose hSearch
    
        LL1.CurrNoDec = SwapLow4AndHigh4(wfd.nFileSize) ' FileSize comes in with low 4 and high 4 reversed.  FileTime is correct.
        MsgBox "FileSize: " & LL1
    
        Dim st As SYSTEMTIME
        FileTimeToSystemTime wfd.ftCreationTime, st
        MsgBox "Old way, creation date: " & DateSerial(st.wYear, st.wMonth, st.wDay) + TimeSerial(st.wHour, st.wMinute, st.wSecond)
    
        LL1.CurrNoDec = wfd.ftCreationTime
        FileTimeToSystemTimePtr ByVal LL1.Ptr, st                 ' <---- This actually tests the use of the ptr property of the LongLong.
        MsgBox "Creation Date: " & DateSerial(st.wYear, st.wMonth, st.wDay) + TimeSerial(st.wHour, st.wMinute, st.wSecond)
    
    
    
        Unload Me
    End Sub
    
    Public Function SwapLow4AndHigh4(c As Currency) As Currency
        CopyMemory SwapLow4AndHigh4, ByVal VarPtr(c) + 4, 4
        CopyMemory ByVal VarPtr(SwapLow4AndHigh4) + 4, c, 4
    End Function
    
    
    I've also attached it as a ZIP file.

    Enjoy, and let me know what you think,
    Elroy
    Attached Files Attached Files
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  7. #7
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: LongLong

    Elroy, been messing around with this a bit. One of the limitations you mentioned is ULARGE_INTEGER compatibility and adding/subtracting when the high bit becomes in play.

    Regarding the high bit, pertaining to longs, we know we can subtract the max unsigned value and XOR the high bit as needed. This is the method used in standard pointer arithmetic. The same logic could be used for the signed LongLong (aka LARGE_INTEGER). However, unlike standard Longs, where we can use the Double value 4294967296# (equivalent of UINT(max)+1) for subtraction, there really isn't a data type larger than LARGE_INTEGER or is there? We could use CDec for the subtraction: &H10000000000000000 is equivalent to ULARGE_INTEGER(max) + 1. But VB won't accept that hex string. It will accept its numerical equivalent in a CDec call: MyVariant = CDec("18446744073709551616")

    Regarding ULARGE_INTEGER. That same CDec call can be used to represent a LARGE_INTEGER, with its high bit set, as a positive number.
    Code:
     ' convert negative LARGE_INTEGER to ULARGE_INTEGER-like value
    Debug.Print CLngLng(-1) + CDec("18446744073709551616")
    When representing &H80808080 (long) as positive, a Long can't be used so we use a Double for example. Similar here, we can't use LARGE_INTEGER because of the sign bit, so we use CDec (VarType = vbDecimal).

    Last note: CDec can use Or, Xor, And, etc with your LongLong, but still has the 64-bit limitation it seems.

    Edited: Sample to help explain a bit
    Code:
     ' for longs, we'd express a negative value as positive via a Double
    MsgBox &H80808080 + 4294967296# ' UINT(max)+1
    Code:
     ' for LongLong we'd express a negative value as positive via CDec variant
    MsgBox CLngLng("&h8080808000000000") + CDec("18446744073709551616") ' ULARGE_INTEGER(max)+1
    Likewise, some pointer-like math (wrapping around the high bit)
    For typical longs, something like this:
    Code:
    Private Function pvSafePointerAdd(thePointer As Long, AmountToAdjust As Double) As Long
        Dim xAdj As Long, xShift As Long
        Const SIGN_BIT = &H80000000
        Const MAXINT_SIGNED As Double = 2147483647#
        Const MAXINT_UNSIGNED As Double = 4294967296#
        
        If AmountToAdjust > MAXINT_SIGNED Or Abs(AmountToAdjust) > MAXINT_SIGNED Then
            xAdj = MAXINT_UNSIGNED - Abs(AmountToAdjust)
            xShift = SIGN_BIT
        Else
            xAdj = Abs(AmountToAdjust)
        End If
        
        If AmountToAdjust > 0# Then
            pvSafePointerAdd = ((thePointer Xor SIGN_BIT) + (xAdj Xor xShift)) Xor (SIGN_BIT Xor xShift)
        ElseIf AmountToAdjust < 0# Then
            pvSafePointerAdd = ((thePointer Xor SIGN_BIT) - (xAdj Xor xShift)) Xor (SIGN_BIT Xor xShift)
        Else
            pvSafePointerAdd = thePointer
        End If
    
    End Function
    For LongLongs, this:
    Code:
    Private Function pvSafePointerAdd_LongLong(thePointer As Variant, AmountToAdjust As Variant) As Variant
        ' returns signed LongLong
        
        Dim xShift As Variant, xAdj As Variant
        Dim vDec As Variant, vSign As Variant
        Const SIGN_BIT As String = "&H8000000000000000"
        Const MAX_SIGNED As String = "&H7FFFFFFFFFFFFFFF"
        Const MAX_UNSIGNED As String = "18446744073709551616"
        
        xAdj = CLngLng(MAX_SIGNED)
        vSign = CLngLng(SIGN_BIT)
        ' note: if adding this to your class; those 2 variables could be cached in Declarations section
        '     so that extra CPU cycles not needed for each call into this routine
        If AmountToAdjust > xAdj Or Abs(AmountToAdjust) > xAdj Then
            xAdj = CLngLng(Abs(AmountToAdjust) - CDec(MAX_UNSIGNED))
            xShift = vSign
        Else
            xAdj = CLngLng(Abs(AmountToAdjust))
            xShift = 0
        End If
    
        If AmountToAdjust > 0 Then
            pvSafePointerAdd_LongLong = CLngLng(((thePointer Xor vSign) + (xAdj Xor xShift)) Xor (vSign Xor xShift))
        ElseIf AmountToAdjust < 0 Then
            pvSafePointerAdd_LongLong = CLngLng(((thePointer Xor vSign) - (xAdj Xor xShift)) Xor (vSign Xor xShift))
        Else
            pvSafePointerAdd_LongLong = CLngLng(thePointer)
        End If
        
    End Function
    Last edited by LaVolpe; Dec 31st, 2017 at 04:38 PM.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  8. #8

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,909

    Re: LongLong

    @LaVolpe: Nice work. I'm not sure I've ever considered one of these LongLong variables being used as a pointer, especially in the case where VB6 will be manipulating them. But they certainly may be used that way. In 64-bit-Office-VBA (or, any 64-bit program for that matter), it seems that that's precisely why they were derived.

    Hmm, also, I think it'll be a while before we encounter computers with enough memory where that sign bit will actually be problematic. Let's see. If &h7FFFFFFFFFFFFFFF is the largest (signed) LongLong positive number, that would be a computer with more than 7 exabytes; or, in language we're more used to hearing, a computer with more than 7,340,032 terabytes of memory! But hey, we'll be ready when it happens.

    Happy New Year,
    Elroy

    EDIT1: Although VB6 will need to jump through some pretty high hurdles to actually address that memory (even below the 7 exabyte range but, say, above the 4 gig range).
    Last edited by Elroy; Dec 31st, 2017 at 06:37 PM.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  9. #9
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: LongLong

    Yeah, point taken. But they (both LARGE_INTEGER & ULARGE_INTEGER) are also used in IStream. Most implementations use the Currency trick, but that has limitations with add/subtracting. This becomes a problem when you start getting to values in excess of 372,036,854,775,807. Here's an interesting article regarding the usage of Currency for LARGE_INTEGER.

    For my own education, I was simulating extremely large streams and bumped up against that Currency limitation.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  10. #10

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,909

    Re: LongLong

    @LaVolpe: You've got me revisiting all this too. I started by looking at your pvSafePointerAdd procedure, and noticed a couple of things:

    1) Is the line ...
    Code:
    
        If AmountToAdjust > MAXINT_SIGNED Or Abs(AmountToAdjust) > MAXINT_SIGNED Then
    
    ... really needed in that form? It would seem that ...

    Code:
    
        If Abs(AmountToAdjust) > MAXINT_SIGNED Then
    
    ... would do just as well.

    2) I can certainly understand an overflow error when we get beyond &hFFFFFFFF. However, we seem to get one a bit earlier. The following ...

    Code:
    
        Debug.Print Hex$(pvSafePointerAdd(&H7FFFFFFF, 2147483648#))
    
    ... seems to crash it. It's just one number (&hFFFFFFFF&), but it does seem that it should handle that.


    Happy 2018,
    Elroy

    EDIT1: Also, the following crashes it (as well as the entire series of "AmountToAdjust" numbers that are smaller (but should still be within range):

    Code:
    
    Debug.Print Hex$(pvSafePointerAdd(&HFFFFFFFF, -2147483648#))
    
    Last edited by Elroy; Jan 1st, 2018 at 03:01 PM.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  11. #11
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,454

    Re: LongLong

    Quote Originally Posted by LaVolpe View Post
    This becomes a problem when you start getting to values in excess of 372,036,854,775,807. ...

    For my own education, I was simulating extremely large streams and bumped up against that Currency limitation.
    Could you describe, how exactly you "bumped up against" that limit,
    because I can't reproduce that here.

    If I test the following in the VB-IDE-Immediate-Window (with regards to the "Display-Limit" the article mentions):
    Code:
    C=CCur("922337203685477"): ? CStr(C), CStr(-C)
    Then the above works just fine (with nearly 3 times the value of 372,036,854,775,807).

    Also the "hints" with regards to multiplication or division are somehow "weird" - because
    once I have a valid Currency-Value, then I can multiply it with an integer directly just fine.
    (not sure, where the factor 10000 should come into it).
    example (again going far over the mentioned "Display-Range" of 372,036,854,775,807)
    Code:
    ?CStr( CCur("102481911520608") * 9 ) 'printing out: 922337203685472
    Olaf

  12. #12
    The Idiot
    Join Date
    Dec 2014
    Posts
    3,002

    Re: LongLong

    in my game, i use numbers up to 999,999,999,999,999 but im using double/variant and without problems with multiplication/division.

  13. #13

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,909

    Re: LongLong

    @LaVolpe: With respect to the problems I found with pvSafePointerAdd, I started looking into "fixing" it, but it started getting complicated. So I just re-wrote it. I believe the following resolves any/all problems other than "natural" overflows.

    Code:
    
    Public Function pvSafePointerAdd(thePointer As Long, AmountToAdjust As Double) As Long
        ' The AmountToAdjust should always be an integer.
        ' Subtraction (a negative AmountToAdjust) works just fine.
        ' This is basically doing addition-or-subtraction on thePointer as if it's UNSIGNED.
        Dim ptr As Variant
        Dim sign As Variant
        '
        sign = CDec(2147483648#)                        ' The sign-bit of a Long, treated as a number (&h80000000).
        '
        ptr = CDec(thePointer And &H7FFFFFFF)
        If thePointer < 0 Then ptr = ptr + sign         ' Treat sign bit as more of the number, all positive.
        '
        ptr = ptr + AmountToAdjust                      ' Perform the adjustment; results is Decimal.
        '
        ' The following may have "natural" overflows when outside of the range &h0 to &hFFFFFFFF.
        If ptr >= sign Then
            pvSafePointerAdd = CLng(ptr - sign) Or &H80000000
        Else
            pvSafePointerAdd = ptr
        End If
    End Function
    
    I haven't started thinking about it just yet, but the same ideas may also work for the LongLong, but we'll still have problems if we want both thePointer and AmountToAdjust to have the possible range of a LongLong. I'll have to think about that one a bit.

    Enjoy,
    Elroy

    EDIT1: Here, I improved it a bit more. This gives you a "wrap" option:

    Code:
    
    Public Function pvSafePointerAdd(thePointer As Long, AmountToAdjust As Double, Optional bWrap As Boolean) As Long
        ' The AmountToAdjust should always be an integer.
        ' Subtraction (a negative AmountToAdjust) works just fine.
        ' This is basically doing addition-or-subtraction on thePointer as if it's UNSIGNED.
        Dim ptr As Variant
        Dim sign As Variant
        Dim signx2 As Variant
        '
        sign = CDec(2147483648#)                        ' The sign-bit of a Long, treated as a number (&h80000000).
        signx2 = sign * 2                               ' This is actually &h100000000, the wrap limit.
        '
        ptr = CDec(thePointer And &H7FFFFFFF)           ' Strip any sign bit and convert to Decimal.
        If thePointer < 0 Then ptr = ptr + sign         ' Treat sign bit as more of the number, all positive.
        '
        ptr = ptr + AmountToAdjust                      ' Perform the adjustment; results is Decimal.
        '
        If bWrap Then
            Do While ptr < 0
                ptr = ptr + signx2
            Loop
            Do While ptr >= signx2
                ptr = ptr - signx2
            Loop
        Else
           If ptr < 0 Then Error 6
           If ptr >= signx2 Then Error 6                ' This catches the one situation where CLng(ptr - sign) may leave only the sign bit turned on.
        End If
        '
        If ptr >= sign Then
            pvSafePointerAdd = CLng(ptr - sign) Or &H80000000
        Else
            pvSafePointerAdd = ptr
        End If
    End Function
    
    Last edited by Elroy; Jan 1st, 2018 at 04:32 PM.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  14. #14
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: LongLong

    Elroy, yep looks like I screwed the pooch a bit on fiddling with some pointer math. When I changed the function parameter from long to double, messed it up royally. Was attempting to allow friendly parameters, i.e., pass 2147483648 double instead of -2147483648 long. I'll tear that down again, but at a later time. Don't really need it for what I'm playing with now. Thanx.

    @Olaf. The 10000 usage is due to the way Currency is stored. If passing a Currency variable to an API that expects an 8-byte value, one must divide the 'real' value by 10000, i.e., passing the currency value of 1@ is actually the value 10000L at memory address.
    Code:
    Dim L(1) As Long
    CopyMemory L(0), 1@, 8
    ' L(0)=10000: L(1)=0
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width