Jump to content

# operator64

## Recommended Posts

I needed this library to continue working on one or two of my projects. Hopefully it will be of use to someone else too. These functions attempt to perform accurate calculations for the largest Int-64 values, testing for integer overflow and resorting to floats when results go out of range. This library is primarily intended to be used with Int-32 or Int-64 values and it offers no advantage whatsoever to pass floats as parameters. It only makes sense if you intend to perform accurate calculations on large Int-64 values. I know this can be done using strings too, but I prefer not to speculate as to the difference in time penalties.

```; #INDEX# ======================================================================================================================
; Title .........: operator64
; AutoIt Version : 3.3.14.0
; Language ......: English
; Description ...: Helper functions for basic maths operations to be used with Int-32 and Int-64 numeric data types.
; Notes .........: Tests for integer overflow and attempts to correct for small inaccuracies affecting certain calculations.
;                  Return values are cast as Int-32 or Int-64 when possible, otherwise the result will be returned as a double.
;                  If it is not possible to return an Int-64 data type, the @extended flag is always set to non zero.
;                  Extended return values indicate that 100% accuracy cannot be guaranteed or that the result is ambiguous.
;                  The following values are valid for all functions in this library
;                  |@error = 1 The input or first operand is not an integer.
;                  |@error = 2 The second operand is not an integer.
;                  |@error = 3 Internal function error (ignore).
;                  |@extended = 1 The result is limited to double precision.
;                  |@extended = 2 The result is not an integer.
;                  |@extended = 3 The result is out of range for Int-64.
;                  |@extended = 4 The result is infinity.
;                  |@extended = 5 The result is an imaginary number.
;                  When an error occurs, the @extended flag is always set to non zero and a value returned regardless.
; Author(s) .....: czardas
; ==============================================================================================================================

; #CURRENT# ====================================================================================================================
; _Abs64
; _Add64
; _Divide64
; _Multiply64
; _Power64
; _Root64
; _Subtract64
; ==============================================================================================================================

; #INTERNAL_USE_ONLY#===========================================================================================================
; __DoubleTo64
; __Integer64
; __OverflowDetect
; __Product64
; __WholeNumberDivision
; ==============================================================================================================================

; #FUNCTION# ===================================================================================================================
; Name...........: _Abs64
; Description ...: Returns the absolute value of an integer.
; Syntax.........: _Abs64(\$iInteger)
; Parameters.....; \$iInteger - The integer to operate on.
; Return values .: Returns an Int-32, or Int-64, value.
;                  Failure returns the executed expression and sets the following flags:
;                  |@error = 1 The input parameter is not an integer.
;                  |@extended = 2 The result is not an integer.
;                  |@extended = 3 The absolute value (2^63) is out of range for Int-64.
; Author.........: czardas
; ==============================================================================================================================

Func _Abs64(\$iInteger)
\$iInteger = __Integer64(\$iInteger)
If @error Then Return SetError(1, 2, Abs(\$iInteger))
If \$iInteger = 0x8000000000000000 Then Return SetExtended(3, Abs(\$iInteger))
Return \$iInteger < 0 ? \$iInteger *-1 : \$iInteger
EndFunc ;==> _Abs64

; #FUNCTION# ===================================================================================================================
; Name...........: _Add64
; Description ...: Adds two integers together.
; Syntax.........: _Add64(\$iOperand_1, \$iOperand_2)
; Parameters.....; \$iOperand_1 - The first operand.
;                  \$iOperand_2 - The second operand.
; Return values .: Returns an Int-32, or Int-64, value.
;                  Failure returns the executed expression and sets the following flags:
;                  |@error = 1 The first parameter is not an integer.
;                  |@error = 2 The second parameter is not an integer.
;                  |@extended = 1 The result is limited to double precision.
;                  |@extended = 3 The result is out of range for Int-64.
; Author.........: czardas
; ==============================================================================================================================

Func _Add64(\$iOperand_1, \$iOperand_2)
\$iOperand_1 = __Integer64(\$iOperand_1)
If @error Then Return SetError(1, 1, __DoubleTo64(\$iOperand_1 + \$iOperand_2)) ; may still return an integer
\$iOperand_2 = __Integer64(\$iOperand_2)
If @error Then Return SetError(2, 1, __DoubleTo64(\$iOperand_1 + \$iOperand_2)) ; ditto

Local \$bOverflow, \$nResult
\$bOverflow = __OverflowDetect('+', \$iOperand_1, \$iOperand_2, \$nResult)
Return SetExtended(\$bOverflow *3, \$nResult)
EndFunc ;==> _Add64

; #FUNCTION# ===================================================================================================================
; Name...........: _Divide64
; Description ...: Divides two integers.
; Syntax.........: _Divide64(\$iOperand_1, \$iOperand_2)
; Parameters.....; \$iOperand_1 - The first operand.
;                  \$iOperand_2 - The second operand.
;                  Failure returns the executed expression and sets the following flags:
;                  |@error = 1 The first parameter is not an integer.
;                  |@error = 2 The second parameter is not an integer.
;                  |@extended = 1 The result is limited to double precision.
;                  |@extended = 2 The result is not an integer.
;                  |@extended = 4 The result is infinity.
; Author.........: czardas
; Comments ......; Due to internal rounding, errors may return an integer with the extended flag set to 2. This is not a bug!
; ==============================================================================================================================

Func _Divide64(\$iOperand_1, \$iOperand_2)
\$iOperand_1 = __Integer64(\$iOperand_1)
If @error Then Return SetError(1, 1, __DoubleTo64(\$iOperand_1 / \$iOperand_2)) ; may still return an integer
\$iOperand_2 = __Integer64(\$iOperand_2)
If @error Then Return SetError(2, 1, __DoubleTo64(\$iOperand_1 / \$iOperand_2)) ; as above

If \$iOperand_1 = 0x8000000000000000 Then ; this is not dealt with by the internal function
If \$iOperand_2 = 0x8000000000000000 Then Return 1 ; \$iOperand_1 is divided by itself

; divide both values by 2 to remain within the specified range of the function __WholeNumberDivision()
\$iOperand_1 = 0xC000000000000000 ; -4611686018427387904
\$iOperand_2 = __WholeNumberDivision(\$iOperand_2, 2)
If @extended Then Return SetExtended(@extended, \$iOperand_1 / \$iOperand_2)
EndIf

Local \$nResult = __WholeNumberDivision(\$iOperand_1, \$iOperand_2)
Return SetExtended(@extended, \$nResult)
EndFunc ;==> _Divide64

; #FUNCTION# ===================================================================================================================
; Name...........: _Multiply64
; Description ...: Multiplies two integers together.
; Syntax.........: _Multiply64(\$iOperand_1, \$iOperand_2)
; Parameters.....; \$iOperand_1 - The first operand.
;                  \$iOperand_2 - The second operand.
; Return values .: Returns an Int-32, or Int-64, value.
;                  Failure returns the executed expression and sets the following flags:
;                  |@error = 1 The first parameter is not an integer.
;                  |@error = 2 The second parameter is not an integer.
;                  |@extended = 2 The result is not an integer.
;                  |@extended = 3 The result is out of range for Int-64.
; Author.........: czardas
; Comments ......; Due to internal rounding, errors may return an integer with the extended flag set to 2. This is not a bug!
; ==============================================================================================================================

Func _Multiply64(\$iOperand_1, \$iOperand_2)
\$iOperand_1 = __Integer64(\$iOperand_1)
If @error Then Return SetError(1, 2, \$iOperand_1 * \$iOperand_2)
\$iOperand_2 = __Integer64(\$iOperand_2)
If @error Then Return SetError(2, 2, \$iOperand_1 * \$iOperand_2)

Local \$iProduct = __Product64(\$iOperand_1, \$iOperand_2)
Return SetExtended(@extended, \$iProduct)
EndFunc ;==> _Multiply64

; #FUNCTION# ===================================================================================================================
; Name...........: _Power64
; Description ...: Raises an integer to the power of a second integer.
; Syntax.........: _Power64(\$iInteger, \$iPower)
; Parameters.....; \$iInteger - The integer to operate on.
;                  \$iPower - The power to raise the integer to.
; Return values .: Returns an Int-32, or Int-64, value.
;                  Failure returns the executed expression and sets the following flags:
;                  |@error = 1 The first parameter is not an integer.
;                  |@error = 2 The second parameter is not an integer.
;                  |@extended = 1 The result is limited to double precision.
;                  |@extended = 3 The result is out of range for Int-64.
; Author.........: czardas
; ==============================================================================================================================

Func _Power64(\$iInteger, \$iPower)
\$iInteger = __Integer64(\$iInteger)
If @error Then Return SetError(1, 1, \$iInteger ^ \$iPower)
\$iPower = __Integer64(\$iPower)
If @error Then Return SetError(2, 1, \$iInteger ^ \$iPower)

If \$iInteger = 1 Or \$iPower = 0 Then Return 1
If \$iPower < 0 Or \$iPower > 63 Then Return SetExtended(1, \$iInteger ^ \$iPower)

Local \$iTriggers = 0, \$iCount = 0, \$iPow = \$iPower

While \$iPow > 1
If Mod(\$iPow, 2) Then
\$iPow -= 1
Else ; to reduce calls to __Product64()
; triggers indicate the product should be squared (see next loop)
\$iTriggers += 2 ^ \$iCount ; set trigger
\$iPow /= 2
EndIf
\$iCount += 1
WEnd

Local \$iOperand, \$iResult = \$iInteger

For \$i = \$iCount -1 To 0 Step -1
; multiply the product either by itself or the original value
\$iOperand = BitAND(\$iTriggers, 2^\$i) ? \$iResult : \$iInteger
\$iResult = __Product64(\$iResult, \$iOperand)
If @extended Then Return SetExtended(3, \$iInteger ^ \$iPower)
Next

Return \$iResult
EndFunc ;==> _Power64

; #FUNCTION# ===================================================================================================================
; Name...........: _Root64
; Description ...: Calculates the nth root of an integer.
; Syntax.........: _Root64(\$iInteger, \$iRoot)
; Parameters.....; \$iInteger - The integer to operate on.
;                  \$iRoot - The root of the integer to calculate.
; Return values .: Returns an Int-32, or Int-64, value.
;                  Failure returns the executed expression and sets the following flags:
;                  |@error = 1 The first parameter is not an integer.
;                  |@error = 2 The second parameter is not an integer.
;                  |@extended = 1 The result is limited to double precision.
;                  |@extended = 2 The result is not an integer.
;                  |@extended = 5 The result is an imaginary number.
; Author.........: czardas
; Comments ......; _Root64() has limited application beyond forcing an integer return value whenever possible.
;                  Roots of negative integers return a defined value if one exists, otherwise the result is always undefined.
; ==============================================================================================================================

Func _Root64(\$iInteger, \$iRoot)
\$iInteger = __Integer64(\$iInteger)
If @error Then Return SetError(1, 1, \$iInteger ^ (1 / \$iRoot))
\$iRoot = __Integer64(\$iRoot)
If @error Then Return SetError(2, 1, \$iInteger ^ (1 / \$iRoot))

If \$iRoot > 63 Or \$iRoot < 1 Then Return SetExtended(1, \$iInteger ^ (1 / \$iRoot)) ; out of range

If \$iRoot = 1 Then Return \$iInteger

Local \$iSign = \$iInteger < 0 ? -1 : 1
If \$iSign = -1 And Not Mod(\$iRoot, 2) Then Return SetExtended(5, \$iInteger ^ (1 / \$iRoot)) ; undefined

If \$iInteger = 0x8000000000000000 Then ; here the absolute value cannot be represented by Int-64
; a small set of defined return values are simply hard coded
Local \$aMaxRoot = [[3,-2097152],[7,-512],[9,-128],[21,-8],[63,-2]] ; there are only five cases
For \$i = 0 To 4
If \$iRoot = \$aMaxRoot[\$i][0] Then Return \$aMaxRoot[\$i][1]
Next
Return SetExtended(2, \$iInteger ^ (1 / \$iRoot)) ; the return value is not an integer
EndIf

; positive values are used to calculate the odd roots of negative integers
Local \$iOperand = \$iSign = 1 ? \$iInteger : \$iInteger *-1

; here the margin of error with Int() is probably too small to necessitate further processing
Local \$iReturn = Int(\$iOperand ^ (1 / \$iRoot))

; check the result
Local \$iCompare = \$iReturn
For \$i = 2 To \$iRoot
\$iCompare *= \$iReturn
Next

; the chances that this comparison will fail seem negligible - see comments above
If \$iCompare <> \$iOperand Then Return SetExtended(2, \$iInteger ^ (1 / \$iRoot)) ; the return value is not an integer

Return \$iReturn * \$iSign
EndFunc ;==> _Root64

; #FUNCTION# ===================================================================================================================
; Name...........: _Subtract64
; Description ...: Subtracts one integer from another.
; Syntax.........: _Subtract64(\$iOperand_1, \$iOperand_2)
; Parameters.....; \$iOperand_1 - The first operand.
;                  \$iOperand_2 - The second operand.
; Return values .: Returns an Int-32, or Int-64, value.
;                  Failure returns the executed expression and sets the following flags:
;                  |@error = 1 The first parameter is not an integer.
;                  |@error = 2 The second parameter is not an integer.
;                  |@extended = 1 The result is limited to double precision.
;                  |@extended = 3 The result is out of range for Int-64.
; Author.........: czardas
; ==============================================================================================================================

Func _Subtract64(\$iOperand_1, \$iOperand_2)
\$iOperand_1 = __Integer64(\$iOperand_1)
If @error Then Return SetError(1, 1, __DoubleTo64(\$iOperand_1 - \$iOperand_2)) ; may still return an integer
\$iOperand_2 = __Integer64(\$iOperand_2)
If @error Then Return SetError(2, 1, __DoubleTo64(\$iOperand_1 - \$iOperand_2)) ; ditto

Local \$bOverflow, \$nResult
\$bOverflow = __OverflowDetect('-', \$iOperand_1, \$iOperand_2, \$nResult)
Return SetExtended(\$bOverflow *3, \$nResult)
EndFunc ;==> _Subtract64

#Region - Internal Functions

; #INTERNAL_USE_ONLY# ==========================================================================================================
; Name...........: __DoubleTo64
; Description ...: Helper function - converts an integer value double to Int-32 or Int-64.
; Syntax.........: __DoubleTo64(\$nValue)
; Parameters.....; \$nValue - The double to convert
; Return values .: Success returns Int-32 or Int-64 when the interpreter evaluates the converted double equal to the input.
;                  Failure returns the input parameter without any modification and sets the following flags:
;                  |@error = 3 Internal error (x2) ==> see comments in the code.
;                  |@extended = 2 Conversion failed ==> the return value is not an integer.
; Author ........: czardas
; Comments ......; Doubles representing integer values greater than 15 digits cannot be relied on for accuracy.
; ==============================================================================================================================

Func __DoubleTo64(\$nValue)
If Not IsNumber(\$nValue) Then Return SetError(3, 2, \$nValue) ; the input is not a number.
If \$nValue > -1.0e+015 And \$nValue < 1.0e+015 Then
Local \$iVal64 = Number(\$nValue, 2) ; convert to Int-64
If \$iVal64 = \$nValue Then ; check to see if conversion was a success [expected range +/- 5.62949953421311e+014]
Return (\$iVal64 > 2147483647 Or \$iVal64 < -2147483648) ? \$iVal64 : Number(\$iVal64, 1) ; Int-64 or Int-32
ElseIf \$iVal64 -1 = \$nValue Then ; attempt to adjust for inaccuracies [subject to possible change]
Return \$iVal64 -1
ElseIf \$iVal64 +1 = \$nValue Then ; as above
Return \$iVal64 +1
EndIf
EndIf

Return SetError(3, 2, \$nValue) ; conversion failed
EndFunc ;==> __DoubleTo64

; #INTERNAL_USE_ONLY# ==========================================================================================================
; Name...........: __Integer64
; Description ...: Checks if a number is an integer and converts doubles to Int-32 or Int-64 if the result appears unambiguous.
; Syntax.........: __Integer64(\$nParam)
; Parameters.....; \$nParam - The number to test
; Return values .: Success returns Int-32 or Int-64 when the interpreter evaluates the returned integer equal to the input.
;                  Failure returns the input parameter without any modification and sets the following flags:
;                  |@error = 1 The input is not an integer.
;                  |@extended = 2 The return value is not an integer.
; Author ........: czardas
; Comments ......; Doubles representing integer values greater than 15 digits cannot be relied on for accuracy.
; ==============================================================================================================================

Func __Integer64(\$nParam)
If Not StringInStr(VarGetType(\$nParam), 'Int') Then
Local \$iInt64 = __DoubleTo64(\$nParam) ; attempt conversion
If @error Then Return SetError(1, 2, \$nParam) ; \$fParam <> \$iInt64
\$nParam = \$iInt64 ; float was compared as being equal to an integer
EndIf
Return \$nParam
EndFunc ;==> __Integer64

; #INTERNAL_USE_ONLY# ==========================================================================================================
; Name...........: __OverflowDetect
; Description ...: Checks for integer overflow on execution of the expression.
; Syntax.........: __OverflowDetect(\$sOperator, \$iOperand_1, \$iOperand_2, \$nResult)
; Parameters.....; \$sOperator - May be +, - or *.
;                  \$iOperand_1 - The first operand.
;                  \$iOperand_2 - The second operand.
;                  \$nResult - [ByRef] Result of executed expression.
; Return values .: Returns True if integer overflow occurs - otherwise returns False. Returns \$nResult ByRef.
; Author ........: czardas
; Comments ......; Int-32 or Int-64 only. No error checks!
; ==============================================================================================================================

Func __OverflowDetect(\$sOperator, \$iOperand_1, \$iOperand_2, ByRef \$nResult)
Local \$iExecute, \$fCompare

Switch \$sOperator
Case '+' ; execute the expression
\$iExecute = \$iOperand_1 + \$iOperand_2
; execute the expression with the operands converted to doubles
\$fCompare = Number(\$iOperand_1, 3) + Number(\$iOperand_2, 3)
Case '-' ; as above
\$iExecute = \$iOperand_1 - \$iOperand_2
\$fCompare = Number(\$iOperand_1, 3) - Number(\$iOperand_2, 3)
Case '*' ; as above
\$iExecute = \$iOperand_1 * \$iOperand_2
\$fCompare = Number(\$iOperand_1, 3) * Number(\$iOperand_2, 3)
EndSwitch

; the results should be approximately equal
Local \$bReturn = StringFormat('%.14e', \$iExecute) <> StringFormat('%.14e', \$fCompare) ; %.15e is too sensitive

\$nResult = \$bReturn ? \$fCompare : \$iExecute
Return \$bReturn
EndFunc ;==> __OverflowDetect

; #INTERNAL_USE_ONLY# ==========================================================================================================
; Name...........: __Product64(\$iOperand_1, \$iOperand_2)
; Description ...: Helper function - multiplies two Int-32 or Int-64 values together.
; Syntax.........: __Product64(\$iOperand_1, \$iOperand_2)
; Parameters.....; \$iOperand_1 - The first integer.
;                  \$iOperand_2 - The second integer.
; Return values .: Returns an Int-32, or Int-64, value.
;                  Failure returns the executed expression and sets the following flags:
;                  |@extended = 3 The result is out of range for Int-64.
; Author.........: czardas
; Comments ......; Int-32 or Int-64 only. No error checks!
; ==============================================================================================================================

Func __Product64(\$iOperand_1, \$iOperand_2)
Local \$bOverflow, \$nResult
\$bOverflow = __OverflowDetect('*', \$iOperand_1, \$iOperand_2, \$nResult)
Return SetExtended(\$bOverflow *3, \$nResult)
EndFunc ;==> __Product64

; #INTERNAL_USE_ONLY# ==========================================================================================================
; Name...........: __WholeNumberDivision
; Description ...: Divides two integers.
; Syntax.........: __WholeNumberDivision(\$iDividend, \$iDivisor)
; Parameters.....; \$iDividend - The first operand.
;                  \$iDivisor - The second operand.
; Return values .: Returns an Int-32, or Int-64, value.
;                  Failure returns the executed expression and sets the following flags:
;                  |@extended = 1 The result is limited to double precision.
;                  |@extended = 2 The result is not an integer.
;                  |@extended = 4 The result is infinity.
; Author ........: czardas
; Comments ......; Input is limited to +/- 9223372036854775807 for both consitency and compatibility with future libraries.
;                  May return an integer with the extended flag set to 2 due to internal rounding. This is not a bug!
; ==============================================================================================================================

Func __WholeNumberDivision(\$iDividend, \$iDivisor) ; Input ranges -9223372036854775807 To 9223372036854775807
If \$iDivisor = 0 Then Return SetExtended(4, \$iDividend / \$iDivisor) ; division by zero

Local \$aDiv = [\$iDividend, \$iDivisor], _
\$iSign = 1

For \$i = 0 To 1
If \$aDiv[\$i] > 0x7FFFFFFFFFFFFFFF Or \$aDiv[\$i] < 0x8000000000000001 Then Return SetExtended(1, \$iDividend / \$iDivisor) ; input range exceeded

If \$aDiv[\$i] < 0 Then ; force positive integers
\$aDiv[\$i] *= -1
\$iSign *= -1 ; to add back later
EndIf
Next

If Mod(\$aDiv[0], \$aDiv[1]) Then Return SetExtended(2, \$iDividend / \$iDivisor) ; not divisible
If \$aDiv[0] = 0 Then Return 0
If \$aDiv[1] = 1 Then Return \$aDiv[0] * \$iSign

Local \$iDivision = Floor(\$aDiv[0] / \$aDiv[1]), \$iDifference, \$iIntegral

While \$iDivision * \$aDiv[1] > \$aDiv[0] ; division is overstated
\$iDifference = (\$aDiv[1] * \$iDivision) - \$aDiv[0]
\$iIntegral = Floor(\$iDifference / \$aDiv[1]) ; avoid shooting beyond the target
If \$iIntegral = 0 Then \$iIntegral = 1 ; prevents hanging in an infinite loop
\$iDivision -= \$iIntegral
WEnd

While \$iDivision * \$aDiv[1] < \$aDiv[0] ; division is understated
\$iDifference = \$aDiv[0] - (\$aDiv[1] * \$iDivision)
\$iIntegral = Floor(\$iDifference / \$aDiv[1]) ; as above
If \$iIntegral = 0 Then \$iIntegral = 1 ; prevents hanging
\$iDivision += \$iIntegral
WEnd

Return \$iDivision * \$iSign
EndFunc ;==> __WholeNumberDivision

#EndRegion```

The following tests needÂ updating because values for @error and @extended have changed.

```#include 'operator64.au3'

ConsoleWrite("Testing _Abs64()" & @LF)                       ; ==> Err  Ext Result
\$test = _Abs64(0xC000000000000000)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   4611686018427387904
\$test = _Abs64(0x8000000000000000)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 2    1   9.22337203685478e+018
\$test = _Abs64(0x8000000000000001)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   9223372036854775807
\$test = _Abs64(-0.123)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 1    1   0.123
;===========================================================

ConsoleWrite(@LF & "Testing _Add64()" & @LF)
\$test = _Add64(0x7FFFFFFFFFFFFFFF, 1)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    1   9.22337203685478e+018
\$test = _Add64(0x7FFFFFFFFFFFFFFF, 0)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   9223372036854775807
\$test = _Add64(0x7FFFFFFFFFFFFFFF, -1)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   9223372036854775806
\$test = _Add64(33, 15)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   48
;===========================================================

ConsoleWrite(@LF & "Testing _Divide64()" & @LF)
\$test = _Divide64(0x7FFFFFFFFFFFFFFF, -1)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   -9223372036854775807
\$test = _Divide64(0x8000000000000000, -2)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   4611686018427387904
\$test = _Divide64(10.0, 2)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   5
\$test = _Divide64(9223372036854775552, -2)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   -4611686018427387776
\$test = _Divide64(9223372036854775552, 0)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 2    1   1.#INF
\$test = _Divide64(675432.0097, -987)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 1    1   -684.328277304965
\$test = _Divide64(6922337, 15)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 4    1   461489.133333333
;===========================================================

ConsoleWrite(@LF & "Testing _Multiply64()" & @LF)
\$test = _Multiply64(9223372036854775552, -2)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    1   -1.84467440737096e+019
\$test = _Multiply64(223372036854775552, 9)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   2010348331692979968
\$test = _Multiply64(1.01, -1)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 1    1   -1.01
;===========================================================

ConsoleWrite(@LF & "Testing _Power64()" & @LF)
\$test = _Power64(-2.0, 63)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   -9223372036854775808
\$test = _Power64(37, 12.0)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   6582952005840035281
\$test = _Power64(17, 19)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    1   2.39072435685151e+023
;===========================================================

ConsoleWrite(@LF & "Testing _Subtract64()" & @LF)
\$test = _Subtract64(1.0, 1)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   0
\$test = _Subtract64(0x8000000000000000, -1)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   -9223372036854775807
\$test = _Subtract64(0x8000000000000000, 0)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    0   -9223372036854775808
\$test = _Subtract64(0x8000000000000000, 1)
ConsoleWrite(@Error & @TAB & @Extended & @TAB & \$test & @LF) ; ==> 0    1   -9.22337203685478e+018```

It's nice to try and figure out something like this for yourself, but it would be quite impossible without the hints and advice I receive from others when I take a wrong turn sometimes. So thanks to those people.

Edited by czardas
##### Share on other sites

New Function added: _Power64()
Some optimization but more is needed. This new function is too slow at the moment.

Example:

```#include 'operator64.au3'

ConsoleWrite(_Power64(-2, 63) & @LF) ; -9223372036854775808```

Â

Edited by czardas
##### Share on other sites

I have optimizedÂ the code for _Power64() as best as I know how. There is still quite a time penalty, but I see this as a trade off for performing accurate calculationsÂ with large integers. I'm sure if there was no time penalty involved then the world would look slightly different.Â I have also changed the behaviour of the UDF: nowÂ all functions acceptÂ integer valueÂ doubles of up to 15 digits.

Edited by czardas
##### Share on other sites

I said I dared not to speculate about speed, but I lied. Â Speed test comparisons with BigNum revealed that operator64 is about 2 to 3 times faster for all maths functions. Of course with operator64, accuracy is limited to the set of whole numbers which can be represented asÂ Int-64 data types. I'm reasonably happy with these results. Perhaps, when I look around,Â I will discoverÂ better solutions to the ones I came up with.Â MaybeÂ in the futureÂ there will be changes to AutoIt which will render some of these functions obsolete. I'm sure there must be plenty of non-native libraries which do a similar job much faster - so be it!Â

```#include 'operator64.au3'
#include 'BigNum.au3'

Local \$iOperand1, \$iOperand2, \$iTimer, \$iIterations = 10000

;=================================================================

; Add 2 numbers
ConsoleWrite('Addition' & @LF)

\$iOperand1 = 70009256455583555
\$iOperand2 = 198116002927730039

;ConsoleWrite(_Add64(\$iOperand1, \$iOperand2) & @LF)
;ConsoleWrite(_BigNum_Add(\$iOperand1, \$iOperand2) & @LF)

\$iTimer = TimerInit()
For \$i = 1 To \$iIterations
_Add64(\$iOperand1, \$iOperand2)
Next
ConsoleWrite('operator64.au3 ... ' & TimerDiff(\$iTimer) & @LF)

\$iTimer = TimerInit()
For \$i = 1 To \$iIterations
_BigNum_Add(\$iOperand1, \$iOperand2)
Next
ConsoleWrite('BigNum.au3 ....... ' & TimerDiff(\$iTimer) & @LF & @LF)

;=================================================================

; Subtract 2 numbers
ConsoleWrite('Subtraction' & @LF)

\$iOperand1 = 198116002927730039
\$iOperand2 = 70009256455583555

;ConsoleWrite(_Subtract64(\$iOperand1, \$iOperand2) & @LF)
;ConsoleWrite(_BigNum_Sub(\$iOperand1, \$iOperand2) & @LF)

\$iTimer = TimerInit()
For \$i = 1 To \$iIterations
_Subtract64(\$iOperand1, \$iOperand2)
Next
ConsoleWrite('operator64.au3 ... ' & TimerDiff(\$iTimer) & @LF)

\$iTimer = TimerInit()
For \$i = 1 To \$iIterations
_BigNum_Sub(\$iOperand1, \$iOperand2)
Next
ConsoleWrite('BigNum.au3 ....... ' & TimerDiff(\$iTimer) & @LF & @LF)

;=================================================================

; Divide 2 numbers
ConsoleWrite('Division' & @LF)

\$iOperand1 = 0x8000000000000000
\$iOperand2 = -8

;ConsoleWrite(_Divide64(\$iOperand1, \$iOperand2) & @LF)
;ConsoleWrite(_BigNum_Div(\$iOperand1, \$iOperand2) & @LF)

\$iTimer = TimerInit()
For \$i = 1 To \$iIterations
_Divide64(\$iOperand1, \$iOperand2)
Next
ConsoleWrite('operator64.au3 ... ' & TimerDiff(\$iTimer) & @LF)

\$iTimer = TimerInit()
For \$i = 1 To \$iIterations
_BigNum_Div(\$iOperand1, \$iOperand2)
Next
ConsoleWrite('BigNum.au3 ....... ' & TimerDiff(\$iTimer) & @LF & @LF)

;=================================================================

; Multiply 2 numbers
ConsoleWrite('Multiplication' & @LF)

\$iOperand1 = 999999996
\$iOperand2 = 234567873

;ConsoleWrite(_Multiply64(\$iOperand1, \$iOperand2) & @LF)
;ConsoleWrite(_BigNum_Mul(\$iOperand1, \$iOperand2) & @LF)

\$iTimer = TimerInit()
For \$i = 1 To \$iIterations
_Multiply64(\$iOperand1, \$iOperand2)
Next
ConsoleWrite('operator64.au3 ... ' & TimerDiff(\$iTimer) & @LF)

\$iTimer = TimerInit()
For \$i = 1 To \$iIterations
_BigNum_Mul(\$iOperand1, \$iOperand2)
Next
ConsoleWrite('BigNum.au3 ....... ' & TimerDiff(\$iTimer) & @LF & @LF)

;=================================================================

; raise a number to a power
ConsoleWrite('Power Operator' & @LF)

\$iOperand1 = -2
\$iOperand2 = 63
\$iIterations = 2000

;ConsoleWrite(_Power64(\$iOperand1, \$iOperand2) & @LF)
;ConsoleWrite(_BigNum_Pow(\$iOperand1, \$iOperand2) & @LF)

\$iTimer = TimerInit()
For \$i = 1 To \$iIterations
_Power64(\$iOperand1, \$iOperand2)
Next
ConsoleWrite('operator64.au3 ... ' & TimerDiff(\$iTimer) & @LF)

\$iTimer = TimerInit()
For \$i = 1 To \$iIterations
_BigNum_Pow(\$iOperand1, \$iOperand2)
Next
ConsoleWrite('BigNum.au3 ....... ' & TimerDiff(\$iTimer) & @LF & @LF)```

Results

```Addition
operator64.au3 ... 611.45171195762
BigNum.au3 ....... 1001.19733099668

Subtraction
operator64.au3 ... 593.831397302383
BigNum.au3 ....... 1295.27405555922

Division
operator64.au3 ... 753.401508513282
BigNum.au3 ....... 1744.577881667

Multiplication
operator64.au3 ... 670.554139638136
BigNum.au3 ....... 1547.65752329044

Power Operator
operator64.au3 ... 1174.47467967118
BigNum.au3 ....... 3246.95088321818```

One further point worth mentioning - this library is intended for calculations which are likely to result in overflow on Int-64, or are that are handled internally by floating point operations:Â which cannot produce accurate results for the whole Int-64 numeric range.

Edited by czardas
##### Share on other sites

Update:

Some values were registering as having overflowedÂ because an internal string comparison was too sensitive. Sensitivity can be reduced downÂ to just a few digits:Â so this constitutes fine tuning rather than an actual bug.

Error values for the function _Divide64() have changed.

Edited by czardas
##### Share on other sites

As I was writing the following,Â function I kept thinking about Route 66 but that's something else. Â I don't really know what use _Root64() might have:Â it just seemed to be missing from the current library. Perhaps the function is superfluous because roots of integers do not suffer from integer overflow - just tiny FP inaccuracies. It does have some added functionality in that it calculates odd roots of negative integers, but its usefulness is limited to calculating roots when you require an integer to be returned and that's probably not such a common occurrenceÂ - hmm!Â You might want to get back to the original value after using _Power64() and check for errors.Â
Â

```#include 'operator64.au3'

ConsoleWrite(_Root64(-6071163615208263051, 11) & @LF)
ConsoleWrite(_Root64(0x8000000000000000, 7) & @LF)
ConsoleWrite(_Root64(4052555153018976267, 13) & @LF)

; #FUNCTION# ===================================================================================================================
; Name...........: _Root64
; Description ...: Calculates the nth root of an integer.
; Syntax.........: _Root64(\$iInteger, \$iRoot)
; Parameters.....; \$iInteger - The integer to operate on.
;                  \$iRoot - The root of the integer to calculate.
; Return values .: Returns an Int-32, or Int-64, value.
;                  Failure returns the executed expression and sets @error as follows:
;                  |@error = 1 The first parameter is not an integer.
;                  |@error = 2 The second parameter is not an integer.
;                  |@error = 3 The result cannot be represented as an integer.
;                  |@error = 4 Undefined - the result cannot be represented - period.
; Author.........: czardas
; Comments ......; Experimental because:
;                  1. Code to accomodate unexpected exceptions was removed due to lack of evidence that a problem exists.
;                  2. _Root64() has limited application beyond forcing an integer return value whenever possible.
;                  Roots of negative integers return a defined value if one exists, otherwise the result is always undefined.
; ==============================================================================================================================

Func _Root64(\$iInteger, \$iRoot)
\$iInteger = __Integer64(\$iInteger)
If @error Then Return SetError(1, 1, \$iInteger ^ (1 / \$iRoot))
\$iRoot = __Integer64(\$iRoot)
If @error Then Return SetError(2, 1, \$iInteger ^ (1 / \$iRoot))

If \$iRoot > 63 Or \$iRoot < 1 Then Return SetError(3, 1, \$iInteger ^ (1 / \$iRoot)) ; out of range

If \$iRoot = 1 Then Return \$iInteger

Local \$iSign = \$iInteger < 0 ? -1 : 1
If \$iSign = -1 And Not Mod(\$iRoot, 2) Then Return SetError(4, 1, \$iInteger ^ (1 / \$iRoot)) ; undefined

If \$iInteger = 0x8000000000000000 Then ; here the absolute value cannot be represented by Int-64
; a small set of defined return values are simply hard coded
Local \$aMaxRoot = [[3,-2097152],[7,-512],[9,-128],[21,-8],[63,-2]] ; there are only five cases
For \$i = 0 To 4
If \$iRoot = \$aMaxRoot[\$i][0] Then Return \$aMaxRoot[\$i][1]
Next
Return SetError(3, 1, \$iInteger ^ (1 / \$iRoot)) ; the return value is not an integer
EndIf

; positive values are used to calculate the odd roots of negative integers
Local \$iOperand = \$iSign = 1 ? \$iInteger : \$iInteger *-1

; here the margin of error with Int() is probably too small to necessitate further processing
Local \$iReturn = Int(\$iOperand ^ (1 / \$iRoot))

; check the result
Local \$iCompare = \$iReturn
For \$i = 2 To \$iRoot
\$iCompare *= \$iReturn
Next

; the chances that this comparison will fail seem negligible - see comments above
If \$iCompare <> \$iOperand Then Return SetError(3, 1, \$iInteger ^ (1 / \$iRoot)) ; the return value is not an integer

Return \$iReturn * \$iSign
EndFunc ;==> _Root64```

Â

Edited by czardas
##### Share on other sites

• 2 weeks later...

Changes to @error and @extended values. Note division by zero no longer returns an error. The values below are consistent throughout the UDF,Â applying to all functions.Â New version in the first post.

```;                  |@error = 1 The input or first operand is not an integer.
;                  |@error = 2 The second operand is not an integer.
;                  |@error = 3 Internal function error (ignore).
;                  |@extended = 1 The result is limited to double precision.
;                  |@extended = 2 The result is not an integer.
;                  |@extended = 3 The result is out of range for Int-64.
;                  |@extended = 4 The result is infinity.
;                  |@extended = 5 The result is an imaginary number.```

Also added _Root64() to the UDF.

I believe this is finished,Â unless future changes to the AutoItÂ core render some of these functions outmoded or superfluous.

Edited by czardas
##### Share on other sites

• 1 year later...

A question about base conversion came up recently. This presentsÂ aÂ good opportunity to write a practical example using some of these functions. It makes good sense to post examples here. I have made a couple of modifications to my original code after running a few tests.

```#include 'operator64.au3'

Func DecToBase(\$iInt, \$iBase)
\$iInt = __Integer64(\$iInt)
If @error Or \$iInt < 0 Then Return SetError(1, 0, '') ; positive integers only

\$iBase = __Integer64(\$iBase)
If @error Or \$iBase < 2 Or \$iBase > 9 Then Return SetError(2, 0, '') ; bases 2 to 9 only

Local \$iRem, \$sRet = ''
While \$iInt >= \$iBase
\$iRem = Mod(\$iInt, \$iBase)
\$sRet = \$iRem & \$sRet
\$iInt = _Divide64((\$iInt - \$iRem), \$iBase)
WEnd
Return \$iInt & \$sRet
EndFunc ;==> DecToBase

Func BaseToDec(\$sInt, \$iBase)
\$iBase = __Integer64(\$iBase)
If @error Or \$iBase < 2 Or \$iBase > 9 Then Return SetError(2, 0, '') ; bases 2 to 9 only

\$sInt = StringRegExpReplace(\$sInt, '(\A0+)(0\z|.+)', '\$2') ; strip leading zeros
If \$sInt == '' Or Not __BaseIsValid(\$sInt, \$iBase) Then Return SetError(1, 0, '') ; out of range or invalid digits

Local \$iRet = 0, \$iLen = StringLen(\$sInt)
For \$i = 1 To \$iLen
\$iRet += StringMid(\$sInt, \$i, 1) * _Power64(\$iBase, \$iLen - \$i)
Next
Return \$iRet
EndFunc ;==> BaseToDec

Func __BaseIsValid(\$sInt, \$iBase) ; bases 2 to 9 only
If StringRegExp(\$sInt, '[^0-' & \$iBase -1 & ']') Then Return False ; non-valid digits detected
Local \$aMaxVal = ['','', _
'111111111111111111111111111111111111111111111111111111111111111', _ ; binary
'2021110011022210012102010021220101220221', _ ; ternary
'13333333333333333333333333333333', _ ; quaternary
'1104332401304422434310311212', _ ; quinary
'1540241003031030222122211', _ ; senary
'22341010611245052052300', _ ; septenary
'777777777777777777777', _ ; octal
'67404283172107811827'] ; nonary

Return StringLen(\$sInt) < StringLen(\$aMaxVal[\$iBase]) Or StringCompare(\$sInt, \$aMaxVal[\$iBase]) <= 0
EndFunc ;==> __BaseIsValid

; =========== Example ===========
Local \$iInt = 9012345678901234567
Local \$sBase6 = DecToBase(\$iInt, 6)

ConsoleWrite("base 6 ... " & \$sBase6 & @LF)
ConsoleWrite("Int 64 ... " & BaseToDec(\$sBase6, 6) & @LF)```

If you want negative numbers, you can remove the sign and put it back after conversion. Bear in mind that the value 0xFFFFFFFFFFFFFFFF is a special case because the positive equivalent results in integer overflow. These functions are intended to be used with decimal integers beyond 15 digits. For smaller numbers, using operator64 may beÂ overkill.Â Beyond Int64 you will need to use BigNum UDF functions.

NOTE : I have only used operator64 functions as and when needed. Overflow does not occur with addition or multiplication because validation ensures that input valuesÂ remain within range.

Original Thread:Â https://www.autoitscript.com/forum/topic/185951-convert-dec-to-binary-but-ones-and-zeros/?do=findComment&comment=1335687

Edited by czardas
Added: strip leading zeros in the function BaseToDec() - to correct a problem with base validation.
##### Share on other sites

• 4 weeks later...

!170Â

```#include 'operator64.au3'

; slightly crazy
For \$i = 21 To 171
ConsoleWrite(\$i & '! ... ' & _Factorial64(\$i) & @LF)
Next

; slightly more crazy
Local \$j
For \$i = 21 To 171
\$j = Random(1, \$i, 1)
ConsoleWrite('_Combinations64('& \$i & ', ' & \$j & ') ... ' & _Combinations64(\$i, \$j) & @LF)
Next

Func _Factorial64(\$iInt)
\$iInt = __Integer64(\$iInt)
If @error Or \$iInt < 0 Then Return SetError(1, 0, '') ; positive integers only

Local \$iResult = 1
For \$i = 1 to \$iInt <= 20 ? \$iInt : 20
\$iResult *= \$i
Next

For \$i = 21 to \$iInt
\$iResult = _Multiply64(\$iResult, \$i)
Next
Return SetExtended(@extended, \$iResult)
EndFunc ; _Factorial64

Func _Combinations64(\$n, \$k)
\$n = __Integer64(\$n)
If @error Or \$n < 1 Then Return SetError(1, 0, '') ; positive integers > 0 only

\$k = __Integer64(\$k)
If @error Or \$k < 1 Then Return SetError(2, 0, '') ; positive integers > 0 only

If \$k > \$n Then Return 0

\$k = _Multiply64(_Factorial64(\$n -\$k), _Factorial64(\$k))
\$n = _Factorial64(\$n)

Return _Divide64(\$n, \$k)
EndFunc ;==> _Combinations64```

There will be small floating point inaccuracies affecting _Combination64()Â with input values > 20. This is to be expected when internal calculations resort to usingÂ large floats. The return valuesÂ in this caseÂ should be considered as being potentially, although not necessarily,Â understated. The larger the input, the more fuzzy the result. BigNum functions return precise results at the cost of speed.

NOTEÂ Operator64 functions attempt to return an integer whenever possible. With large return values, there is a seamless transition from integers to floats:Â which is one of the main points of this UDF.

Edited by czardas

## Create an account or sign in to comment

You need to be a member in order to leave a comment

## Create an account

Sign up for a new account in our community. It's easy!

Register a new account

## Sign in

Already have an account? Sign in here.

Sign In Now
• ### Recently Browsing   0 members

• No registered users viewing this page.
• ### Similar Content

• #### RegExp Catch and Operator Question - (Moved)

By junichironakashima,

• 11 replies
• 2,116 views
• #### I have a syntax error in an operation! Give me a tiny help, please!

By Rhazz,

• 9 replies
• 2,892 views
• #### While Not Func1() And Func2() vs While Not Func1() And Not Func2()

By guestscripter,

• 3 replies
• 2,125 views
• #### Error check - operators question

By careca,

• 16 replies
• 3,367 views
• #### ImageSearch Until. (Operator Question)

By Schoening,

• 4 replies
• 6,024 views
×

• Wiki

• Back

• #### Beta

• Git
• FAQ
×
• Create New...