# Visual Basic > Visual Basic 6 and Earlier >  [RESOLVED] LongLong Pow (^) function

## Elroy

I know that I could do this, but it probably wouldn't be very fast.  So, while I'm working on a couple of other things, I'm just tossing it out there.

Basically, treating a Currency "as if" it's a LongLong, I'd like a function that starts like the following:



```

Function LLPow(llBase As Currency, bbExponent As Byte) As Currency
    If bbExponent = 0 Then LLPow = 0.0001@: Exit Function
    If llBase = 0@ Then Exit Function
    If bbExponent = 1 Then LLPow = llBase:  Exit Function


    ' ?????


End Function


```

Notice that I forced the exponent to a Byte type.  This solves two problems: 1) staying with integers (LongLong), we don't want to mess with negative exponents, and 2) even a LongLong (of abs>=2) will overflow long before an exponent of 256 (max of Byte).

Sure, I could just multiply in a loop, but there's got to be a more efficient way to do it.

Also, this thread is NOT about returning Double, Single, or Decimal types.  I can rather easily work those out, and have confidence that they're fast.  It's just this LongLong *in* and LongLong *out* that's going to take some thought.

p.s.  It needs to error with error 6 if it overflows.

EDITED:  I added a couple of lines to get things started.

----------


## baka

whats wrong with currency = value ^ exp?

----------


## Elroy

The more I think about this, I think it's just bit-shifting with some special handling of the sign bit.  If nobody else does, I'll probably get this figured out in the next day or two.

Also, here are some "helper" functions for those looking at it.



```

Option Explicit
Private Declare Function GetMem8 Lib "msvbvm60" (ByRef Source As Any, ByRef Dest As Any) As Long ' Always ignore the returned value, it's useless.
Private Type TwoLongs
    lLo As Long
    lHi As Long
End Type

Function LLToHex(llValue As Currency) As String
    Dim u As TwoLongs
    GetMem8 llValue, u
    LLToHex = VBA.Hex$(u.lLo)
    If u.lHi Then LLToHex = Hex$(u.lHi) & Right$("0000000" & LLToHex, 8&)
End Function

Function LLToStr(llValue As Currency) As String
    LLToStr = CStr(CDec(llValue) * CDec(10000))
End Function



```

----------


## Elroy

> whats wrong with currency = value ^ exp?


*value ^ exp* returns a Double and not a Currency, with the corresponding loss of precision.

ADDED:  Baka, granted, 52 bits of precision versus 63 bits of precision isn't a huge difference.  However, I'd like all 63 bits of precision to claim it's a true LongLong integer power.

----------


## baka

why not just make a loop
example



```
Function Pow(Value As Currency, ByVal Exp As Byte) As Currency
    On Error GoTo err:
    Pow = 1
    While Exp > 0
        Pow = Pow * Value
        Exp = Exp - 1
    Wend
    Exit Function
err:
    Pow = 0
End Function
```

----------


## The trick

```
Private Function pow64(ByVal cBase As Currency, ByVal bExp As Byte) As Currency
    Dim cRet    As Currency
    Dim bOvflow As Long
    
    cRet = 0.0001@
    
    Do While True
    
        If bExp And 1 Then
        
            cRet = mul64(cRet, cBase, bOvflow)
            
            If bOvflow Then
                Err.Raise 6
            End If
            
        End If
        
        bExp = bExp \ 2
        
        If bExp = 0 Then
            Exit Do
        End If
        
        cBase = mul64(cBase, cBase, bOvflow)
        
        If bOvflow Then
            Err.Raise 6
        End If
            
    Loop
    
    pow64 = cRet
    
End Function
```

----------


## Elroy

> why not just make a loop
> example
> 
> 
> 
> ```
> Function Pow(Value As Currency, ByVal Exp As Byte) As Currency
>     On Error GoTo err:
>     Pow = 1
> ...


Hi Baka.  You can't just multiply a Currency that you're treating as a LongLong.  You could convert to Decimal, multiply by 10000, and then multiply, but that would cut all the speed out of things.  I'm working with The Trick to get a thunk going to do the LongLong multiplication.

----------


## Elroy

Here's something I found that looks pretty good.


Reference.

It's still dependent on fast LongLong multiplication, but I'll work with what I've got and implement (and test) this.

EDIT:  I will need to consider negative numbers (i.e., 2s complement), but that's not too tough.

----------


## The trick

> Here's something I found that looks pretty good.
> 
> 
> Reference.
> 
> It's still dependent on fast LongLong multiplication, but I'll work with what I've got and implement (and test) this.


Did you see 6 post?  :Smilie:

----------


## Elroy

> Did you see 6 post?


haha, yeah, I did.  But I must confess that I didn't study it in detail.  I saw it had the mul64() in it so I was waiting until we got that sorted first.   :Smilie: 

EDIT:  And yeah, I see it's the same thing.

----------


## Elroy

Here's what I came up with, which is basically what The Trick and that site recommended:



```

Friend Function Pow(ByVal llBase As Currency, ByVal bbExponent As Byte) As Currency
    If bbExponent = mbbZero Then Pow = 0.0001@: Exit Function
    If llBase = 0@ Then Exit Function
    If bbExponent = mbbOne Then Pow = llBase:  Exit Function
    '
    ' Let's strip the sign bit and deal with it later.
    ' Note, if llBase=mcSmallestNeg then overflow error on llBase=-llBase, which is ok.
    Dim Sign As Boolean
    If llBase < 0@ Then
        llBase = -llBase
        If bbExponent And mbbOne Then Sign = True
    End If
    '
    Pow = 0.0001@
    Do While bbExponent
        If bbExponent And mbbOne Then
            Pow = Mult(Pow, llBase)                 ' This could overflow, which is fine.
            bbExponent = bbExponent - mbbOne
        End If
        bbExponent = bbExponent \ mbbTwo
        llBase = Mult(llBase, llBase)               ' This could overflow, which is fine.
    Loop
    If Sign Then Pow = -Pow                         ' This actually can't overflow.
End Function


```

Just for some completeness (and to save a bit of conversion/casting):



```

Dim mbbZero         As Byte: mbbZero = CByte(0)
Dim mbbOne          As Byte: mbbOne = CByte(1)
Dim mbbTwo          As Byte: mbbTwo = CByte(2)

```

And that call to Mult is still being worked on.

I do think this is fairly well optimized though (once Mult is optimized).

----------


## baka

ok, I got it. u mean u want to use the whole currency range, from 0.0001@ up
well u can start with 0.0001@



```
Function Pow(Value As Currency, ByVal Exp As Byte) As Currency
    On Error GoTo err:
    Pow = 0.0001@
    While Exp > 0
        Pow = Pow * Value
        Exp = Exp - 1
    Wend
    Exit Function
err:
    Pow = 0
End Function
```

this should be the most basic and slowest of the lot. but at least understandable.
but, 

2 ^ 2 = 4
while the result of this function is 0.0004

also, this can be used as a reference for speed-testing.

also using bitwise it could go a lot faster instead of *

----------


## georgekar

This is my test. We can use long long in VB6.




```
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
    lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
Private Declare Sub GetMem4 Lib "msvbvm60" (ByVal Addr As Long, retval As Long)
Private Declare Sub PutMem4 Lib "msvbvm60" (ByVal Addr As Long, ByVal NewVal As Long)
Private Enum VARENUM
   VT_I8 = &H14
End Enum
Private Type Dec_Hdr
    DecType     As Integer
    DecScale    As Byte
    DecSign     As Byte
End Type
Public Function cInt64(v As Variant) As Variant
    Dim DecHdr As Dec_Hdr, m As Long
    On Error GoTo er111
    cInt64 = CDec(v)
     If VarType(v) = vbString Then
        If InStr(1, v, "&h", vbTextCompare) = 1 Then
            Do
                m = Len(v)
                v = Replace(v, "&H0", "&H", , , vbTextCompare)
            Loop Until Len(v) = m Or m < 5
            If m = 10 Then
                If cInt64 < 0 Then
                    cInt64 = CDec("&H100000000") + cInt64
                End If
            End If
            
        End If
    End If
    CopyMemory DecHdr, ByVal VarPtr(cInt64), LenB(DecHdr)
    If DecHdr.DecScale Then
    cInt64 = Fix(cInt64)
    End If
    GetMem4 VarPtr(cInt64) + 12, m
    If VarType(v) = vbString Then If m < 0 And Len(v) <= 10 Then GoTo er111
    PutMem4 VarPtr(cInt64), VT_I8
    If (DecHdr.DecSign <> 0) And (cInt64 > 0) Then cInt64 = -cInt64
    If (VarType(cInt64) <> VT_I8) Then Err.Raise 6
    Exit Function
er111:
     Err.Raise Err.Number
End Function


Sub TestExp()
Const vbLongLong = 20
' for X=25, N=5     R=  298023223876953125 (long long)
' for X=26, N=5     R= 1490116119384765625 (long long)
' for X=27, N=5     R= 7450580596923828125 (long long)
' for x=28, N=5     R=3,72529029846191E+19 (double)
x = cInt64(27)
N = cInt64(5)
R = cInt64(1)
Debug.Print VarType(x) = vbLongLong, VarType(N) = vbLongLong, VarType(R) = vbLongLong
On Error GoTo 100
While x > 0: If x Mod 2 = 1 Then x = x - 1: R = R * N
x = x \ 2: If x > 0 Then N = N * N
Wend
Debug.Print VarType(x) = vbLongLong, VarType(N) = vbLongLong, VarType(R) = vbLongLong
Debug.Print R, R = cInt64("7450580596923828125")
Exit Sub
100 If Err Then Debug.Print Err.Description: Err.Clear
End Sub
```

PS comparisons works too

----------


## Elroy

Ok, I think this one is done.  Lots of good work though.   :Smilie:

----------


## georgekar

This one if you prefer Currency converter to cint64. (Add to the code above)
Add/replace this


```
Debug.Print R, R = cInt64("7450580596923828125"), Copy64Cur(R)
Debug.Print CopyCur64(Copy64Cur(R))
```





```
Private Declare Sub GetMem81 Lib "msvbvm60" Alias "GetMem8" (ByVal Addr As Long, retval As Currency)
```





```
Function Copy64Cur(ByVal X) As Currency
Dim L As Long
If VarType(X) = 20 Then
GetMem81 VarPtr(X) + 8, Copy64Cur
End If
End Function
Function CopyCur64(ByVal c As Currency)
    Static LL
    If VarType(LL) <> 20 Then LL = cInt64(1)
    CopyMemory ByVal VarPtr(LL) + 8, ByVal VarPtr(c), 8&
    CopyCur64 = LL
End Function
```

----------

