2014-09-22 2 views
0

불행히도 Excel 용 VBA에서는이 모든 작업을 수행해야하지만, 다른 언어에서는 일반적으로 부호없는 정수 인 숫자를 가져 오는 방법이 있는지 알아 내려고 노력하고 있습니다 (추가, 곱하기, 등)을 VBA에서 double으로 변환 한 다음 다시 VBA에서 Long으로 변환하면 비트 연산 (특정 xor)을 수행 할 수 있도록 부호없는 long과 같은 비트가됩니다.비트 연산의 경우 double에서 integer로 변환 하시겠습니까?

내가 할 수 있다면 기꺼이 DLL을 호출 할 수 있지만이 환경에서는 가능하지 않을 것입니다.

이 문제에 대한 의견이 있으십니까?

+1

왜 두 개가 필요합니까? 그 범위는 무엇입니까? ['Decimal' 데이터 형식 사용을 고려 했습니까?] (http://msdn.microsoft.com/en-us/library/xtba3z33.aspx) –

+0

숫자가 2^31보다 크지 만 2^32. – supercheetah

+0

'Decimal' 타입에 대해서는 몰랐습니다. 나는 그걸 가지고 놀 것이다. 효과가있을 수 있습니다. – supercheetah

답변

1

VBA에서 해시를 구현하는 동안 비슷한 요구가있었습니다. 나는 교대, 사기 및 다중 바이트 논리 연산의 부족에 좌절감을 느꼈다. 나는 ByteSet 클래스를 생성하고이를 사용하여 CDbltoLng 함수를 작성했습니다.

다음은 변환 함수입니다. 복식의 형식에 대한 정보는 here에서 확인할 수 있습니다. 표준 모듈에 넣으십시오.

Public Function CDblToLng(num As Double) As Long 

    Dim DblBytes As clsByteSet 
    Set DblBytes = New clsByteSet 
    DblBytes.fromDouble num 

    Dim SignMask As clsByteSet 
    Dim ExponentMask As clsByteSet 
    Dim MantissaMask As clsByteSet 

    Set SignMask = New clsByteSet 
    Set ExponentMask = New clsByteSet 
    Set MantissaMask = New clsByteSet 

    SignMask.fromCustomBytes &H80, 0, 0, 0, 0, 0, 0, 0 
    ExponentMask.fromCustomBytes &H7F, &HF0, 0, 0, 0, 0, 0, 0 
    MantissaMask.fromCustomBytes 0, &HF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF 

    Dim negative As Byte 
    negative = DblBytes.Clone.AND_ByteSet(SignMask).ShiftRight(63).toByte 

    Dim ExponentInteger As Integer 
    ExponentInteger = DblBytes.Clone.AND_ByteSet(ExponentMask).ShiftRight(52).toInteger - 1023 

    Dim LongNumber As Long 
    LongNumber = DblBytes.Clone.AND_ByteSet(MantissaMask).ShiftRight(52 - ExponentInteger).toLong 

    If negative Then 
     If ExponentInteger = 31 Then 
      CDblToLng = (Not (LongNumber Or &H80000000)) + 1 
     Else 
      CDblToLng = (Not (LongNumber Or 2^ExponentInteger)) + 1 'Or (IIf(negative, -1, 1) * 2^ExponentInteger) 
     End If 
    Else 
     If ExponentInteger = 31 Then 
      CDblToLng = LongNumber Or &H80000000 
     Else 
      If ExponentInteger <= 30 Then 
       CDblToLng = LongNumber Or 2^ExponentInteger 
      Else 
       CDblToLng = LongNumber 
      End If 
     End If 
    End If 
End Function 

여기에 clsByteSet입니다. VBA에서 거의 모든 숫자 데이터 유형의 바이트를 가져 와서 필요에 따라 바이트를 조작 할 수 있습니다.

Option Compare Database 

'Updated to be a Fluent Interface 

Private Declare Sub CopyMemory Lib "Kernel32" Alias "RtlMoveMemory" (dest As Any, source As Any, ByVal length As Long) 

Private m_arrBytes() As Byte 

Public Function Resize(n As Long) As clsByteSet 
    ReDim m_arrBytes(0 To n - 1) 
End Function 

Public Function fromCustomBytes(ParamArray bytes()) As clsByteSet 
    ReDim m_arrBytes(0 To UBound(bytes)) 
    For i = 0 To UBound(bytes) 
     m_arrBytes(i) = CByte(bytes(i)) 
    Next 

    Set fromCustomBytes = Me 
End Function 

Public Function fromDouble(Dbl As Double) As clsByteSet 
    ReDim m_arrBytes(0 To 7) 
    For i = 0 To 7 
     CopyMemory ByVal VarPtr(m_arrBytes(i)), ByVal CLng(VarPtr(Dbl) + (7& - i)), 1 
    Next 

    Set fromDouble = Me 
End Function 

Public Function fromLong(lng As Long) As clsByteSet 
    ReDim m_arrBytes(0 To 3) 
    For i = 0 To 3 
     CopyMemory ByVal VarPtr(m_arrBytes(i)), ByVal CLng(VarPtr(lng) + (3& - i)), 1 
    Next 

    Set fromLong = Me 
End Function 

Public Function fromInteger(intgr As Integer) As clsByteSet 
    ReDim m_arrBytes(0 To 1) 
    For i = 0 To 1 
     CopyMemory ByVal VarPtr(m_arrBytes(i)), ByVal CLng(VarPtr(intgr) + (1& - i)), 1 
    Next 

    Set fromInteger = Me 
End Function 

Public Function fromByte(b As Byte) As clsByteSet 
    ReDim m_arrBytes(0 To 1 - 1) 
    m_arrBytes(0) = b 

    Set fromByte = Me 
End Function 

Public Function fromBytes(b() As Byte) As clsByteSet 
    ReDim m_arrBytes(LBound(b) To UBound(b)) 
    For i = LBound(b) To UBound(b) 
     m_arrBytes(i) = b(i) 
    Next 

    Set fromBytes = Me 
End Function 

Public Property Get bytes() As Byte() 
    bytes = m_arrBytes 
End Property 

Public Property Get bytesbyte(index As Long) As Byte 
    bytesbyte = m_arrBytes(index) 
End Property 

Public Function Clone() As clsByteSet 
    Set Clone = New clsByteSet 
    Clone.fromBytes m_arrBytes 
End Function 

Public Function toBytes() As Byte() 
    ReDim toBytes(LBound(m_arrBytes) To UBound(m_arrBytes)) 
    For i = LBound(m_arrBytes) To UBound(m_arrBytes) 
     toBytes(i) = m_arrBytes(i) 
    Next 
End Function 

Public Function toByte() As Byte 
    Dim b As Byte 
    b = m_arrBytes(UBound(m_arrBytes)) 
    toByte = b 
End Function 

Public Function toInteger() As Integer 
    Dim intgr As Integer 
    For i = 0 To 1 
     CopyMemory ByVal CLng(VarPtr(intgr) + (1& - i)), ByVal VarPtr(m_arrBytes(i + UBound(m_arrBytes) - 1)), 1 
    Next 
    toInteger = intgr 
End Function 

Public Function toLong() As Long 
    Dim lng As Long 
    For i = 0 To 3 
     CopyMemory ByVal CLng(VarPtr(lng) + (3& - i)), ByVal VarPtr(m_arrBytes(i + UBound(m_arrBytes) - 3)), 1 
    Next 
    toLong = lng 
End Function 

Public Function toDouble() As Double 
    Dim Dbl As Double 
    For i = 0 To 7 
     CopyMemory ByVal CLng(VarPtr(Dbl) + (7& - i)), ByVal VarPtr(m_arrBytes(i + UBound(m_arrBytes) - 7)), 1 
    Next 
    toDouble = Dbl 
End Function 

Public Function toString() As String 
    Dim strOutput As String 
    Dim i As Long 
    If UBound(m_arrBytes) > 0 Then 
     strOutput = right("0" & Hex(m_arrBytes(0)), 2) 
     i = 1 
     While i <= UBound(m_arrBytes) 
      strOutput = strOutput & " " & right("0" & Hex(m_arrBytes(i)), 2) 
      i = i + 1 
     Wend 
    End If 
    toString = strOutput 
End Function 

'************************************************************************************************************************************ 
'* Bitwise Boolean * 
'******************* 

Public Function XOR_ByteSet(bs As clsByteSet) As clsByteSet 
    For i = 0 To UBound(bs.bytes) 
     m_arrBytes(i) = m_arrBytes(i) Xor bs.bytes(i) 
    Next 

    Set XOR_ByteSet = Me 
End Function 

Public Function AND_ByteSet(bs As clsByteSet) As clsByteSet 
    Dim i As Long 
    For i = 0 To UBound(bs.bytes) 
     m_arrBytes(i) = m_arrBytes(i) And bs.bytesbyte(i) 
    Next 

    Set AND_ByteSet = Me 
End Function 

Public Function OR_ByteSet(bs As clsByteSet) As clsByteSet 
    For i = 0 To UBound(bs.bytes) 
     m_arrBytes(i) = m_arrBytes(i) Or bs.bytes(i) 
    Next 

    Set OR_ByteSet = Me 
End Function 


'************************************************************************************************************************************ 
'* Shifts and Rotates * 
'********************** 

Public Function ShiftRight(length As Long) As clsByteSet 
'Inefficient because it performs two operations: shift bytes then shift bits 
    If length > UBound(m_arrBytes) + 1 Then 
     'Error 
    End If 

    Dim shiftbits As Byte 
    Dim shiftbytes As Long 


    shiftbytes = length \ 8 
    shiftbits = length Mod 8 

    Dim i As Long 

    If shiftbytes > 0 Then 

     For i = UBound(m_arrBytes) To shiftbytes Step -1 
      m_arrBytes(i) = m_arrBytes(i - shiftbytes) 
     Next 
     For i = shiftbytes - 1 To 0 Step -1 
      m_arrBytes(i) = 0 
     Next 

    End If 

    If shiftbits > 0 Then 

     For i = UBound(m_arrBytes) To 1 Step -1 
      m_arrBytes(i) = ShiftByteRight(m_arrBytes(i), shiftbits) Or ShiftByteLeft(m_arrBytes(i - 1), 8 - shiftbits) 
     Next 
     m_arrBytes(0) = ShiftByteRight(m_arrBytes(i), shiftbits) 

    End If 

    Set ShiftRight = Me 
End Function 

Public Function ShiftLeft(length As Long) As clsByteSet 
'Inefficient because it performs two operations: shift bytes then shift bits 
    If length > UBound(m_arrBytes) + 1 Then 
     'Error 
    End If 
    Dim shiftbits As Byte 
    Dim shiftbytes As Long 

    shiftbytes = length \ 8 
    shiftbits = length Mod 8 

    Dim i As Long 

    If shiftbytes > 0 Then 

     For i = 0 To UBound(m_arrBytes) - shiftbytes 
      m_arrBytes(i) = m_arrBytes(i + shiftbytes) 
     Next 
     For i = UBound(m_arrBytes) - shiftbytes To UBound(m_arrBytes) 
      m_arrBytes(i) = 0 
     Next 

    End If 

    If shiftbits > 0 Then 

     For i = 0 To UBound(m_arrBytes) - 1 
      m_arrBytes(i) = ShiftByteLeft(m_arrBytes(i), shiftbits) Or ShiftByteRight(m_arrBytes(i + 1), 8 - shiftbits) 
     Next 
     m_arrBytes(i) = ShiftByteLeft(m_arrBytes(i), shiftbits) 

    End If 

    Set ShiftLeft = Me 
End Function 

Public Function RotateRight(length As Long) As clsByteSet 
'Inefficient because it performs two operations: shift bytes then shift bits 
    If length > (UBound(m_arrBytes) + 1) * 8 Then 
     length = length Mod (UBound(m_arrBytes) + 1) 
    End If 
    Dim shiftbits As Byte 
    Dim shiftbytes As Long 

    shiftbytes = length \ 8 
    shiftbits = length Mod 8 

    Dim i As Long 

    If shiftbytes > 0 Then 

     Dim temparr() As Byte 
     ReDim temparr(0 To shiftbytes - 1) 
     For i = 0 To shiftbytes - 1 
      temparr(i) = m_arrBytes(i + (UBound(m_arrBytes) - (shiftbytes - 1))) 
     Next 
     For i = UBound(m_arrBytes) To shiftbytes Step -1 
      m_arrBytes(i) = m_arrBytes((i - shiftbytes)) 
     Next 
     For i = shiftbytes - 1 To 0 Step -1 
      m_arrBytes(i) = temparr(i) 
     Next 

    End If 

    If shiftbits > 0 Then 

     Dim tempbyte As Byte 
     tempbyte = ShiftByteLeft(m_arrBytes(UBound(m_arrBytes)), 8 - shiftbits) 
     For i = UBound(m_arrBytes) To 1 Step -1 
      m_arrBytes(i) = ShiftByteLeft(m_arrBytes(i - 1), 8 - shiftbits) Or ShiftByteRight(m_arrBytes(i), shiftbits) 
     Next 
     m_arrBytes(0) = ShiftByteRight(m_arrBytes(0), shiftbits) Or tempbyte 

    End If 

    Set RotateRight = Me 
End Function 

Public Function RotateLeft(length As Long) As clsByteSet 
'Inefficient because it performs two operations: shift bytes then shift bits 
    If length > (UBound(m_arrBytes) + 1) * 8 Then 
     length = length Mod (UBound(m_arrBytes) + 1) 
    End If 
    Dim shiftbits As Byte 
    Dim shiftbytes As Long 

    shiftbytes = length \ 8 
    shiftbits = length Mod 8 

    Dim i As Long 

    If shiftbytes > 0 Then 

     Dim temparr() As Byte 
     ReDim temparr(0 To shiftbytes - 1) 
     For i = 0 To shiftbytes - 1 
      temparr(i) = m_arrBytes(i) 
     Next 
     For i = 0 To UBound(m_arrBytes) - shiftbytes 
      m_arrBytes(i) = m_arrBytes((i + shiftbytes)) 
     Next 
     For i = 0 To shiftbytes - 1 
      m_arrBytes(i + UBound(m_arrBytes) - (shiftbytes - 1)) = temparr(i) 
     Next 

    End If 

    If shiftbits > 0 Then 

     Dim tempbyte As Byte 
     tempbyte = ShiftByteRight(m_arrBytes(0), 8 - shiftbits) 
     For i = 0 To UBound(m_arrBytes) - 1 
      m_arrBytes(i) = ShiftByteLeft(m_arrBytes(i), shiftbits) Or ShiftByteRight(m_arrBytes(i + 1), 8 - shiftbits) 
     Next 
     m_arrBytes(UBound(m_arrBytes)) = ShiftByteLeft(m_arrBytes(UBound(m_arrBytes)), shiftbits) Or tempbyte 

    End If 

    Set RotateLeft = Me 
End Function 

Private Function ShiftByteRight(ByVal data As Byte, length As Byte) As Byte 
    ShiftByteRight = data \ (2^(length)) 
End Function 

Private Function ShiftByteLeft(ByVal data As Byte, length As Byte) As Byte 
    ShiftByteLeft = (data And ((2^(8 - length)) - 1)) * (2^length) 
End Function 
+0

그건 내 알고리즘 시간 복잡성을 상당히 증가 시키지만, 내가 원하는 것을 수행한다. – supercheetah

+0

크기를'CopyMemory'의 마지막 매개 변수로 지정하는 대신'm_arrBytes'로 복사 할 때 왜'clsByteSet'에서 객체의 모든 바이트를 반복합니까? – supercheetah

+0

@ supercheetah이 위키 백과 문서보기 : [엔디 언] (http://en.wikipedia.org/wiki/Endianness). Double/Long/Integer의 바이트는 Little Endian에 메모리에 저장됩니다. 예를 들어 long 값 & H0A0B0C0D는 4 개의 개별 바이트로 | 0D | 0C | 0B | 0A |의 역순으로 저장됩니다. 지금 살펴보면 Win32 API가 더 나은 방법을 제공해야한다고 확신하지만 클래스의 인터페이스를 완성하고 나중에 구현을 최적화하는 것에 대해 걱정하고 싶습니다. 제안이 있으시면 알려주세요. – Blackhawk

관련 문제