11 Years of Service
51%
[HIDE-THANKS]UDF:
Examples:
[/HIDE-THANKS]
Code:
>; #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 in this library simply indicate that 100% accuracy cannot be guaranteed.
; 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
; _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 @error as follows:
; |@error = 1 The input parameter is niether Int-32 nor Int-64.
; |@error = 2 This 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, 1, Abs($iInteger))
If $iInteger = 0x8000000000000000 Then Return SetError(2, 1, 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 @error as follows:
; |@error = 1 The first parameter is not an integer.
; |@error = 2 The second parameter is not an integer.
; 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, $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 @error as follows:
; |@error = 1 The first parameter is not an integer.
; |@error = 2 The second parameter is not an integer.
; |@error = 3 The divisor cannot be zero.
; |@error = 4 Input range exceeded.
; |@error = 5 Parameters are not divisible.
; Author.........: czardas
; ==============================================================================================================================
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 @error Then Return SetExtended(1, $iOperand_1 / $iOperand_2)
EndIf
Local $nResult = __WholeNumberDivision($iOperand_1, $iOperand_2)
Return SetError(@error, @error ? 1 : 0, $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 @error as follows:
; |@error = 1 The first parameter is not an integer.
; |@error = 2 The second parameter is not an integer.
; Author.........: czardas
; ==============================================================================================================================
Func _Multiply64($iOperand_1, $iOperand_2)
$iOperand_1 = __Integer64($iOperand_1)
If @error Then Return SetError(1, 1, $iOperand_1 * $iOperand_2)
$iOperand_2 = __Integer64($iOperand_2)
If @error Then Return SetError(2, 1, $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 @error as follows:
; |@error = 1 The first parameter is not an integer.
; |@error = 2 The second parameter is not an integer.
; 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(1, $iInteger ^ $iPower)
Next
Return $iResult
EndFunc ;==> _Power64
; #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 @error as follows:
; |@error = 1 The first parameter is not an integer.
; |@error = 2 The second parameter is not an integer.
; 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, $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 an Int-32 or Int-64 when the interpreter evaluates the return value equal to the input.
; Failure returns the input parameter without any modification and sets @error as follows:
; |@error = 1 The input has to be a number.
; |@error = 2 The input is a float and conversion to an integer failed.
; 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(1, 0, $nValue) ; limited to numeric input
If $nValue > -1.0e+015 And $nValue < 1.0e+015 Then
Local $iVal64 = Number($nValue, 2) ; 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(2, 0, $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 an Int-32 or Int-64 when the interpreter evaluates the return value equal to the input.
; Failure returns the input parameter without any modification and sets @error as follows:
; |@error = 1 The input could not be converted to an exact 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, 0, $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 1 if integer overflow occurs - otherwise returns 0. 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 *1 ; covert to a number
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.
; 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, $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-64 value.
; Failure sets @error as follows:
; |@error = 3 The divisor cannot be zero.
; |@error = 4 Input range exceeded.
; |@error = 5 parameters are not divisible.
; Author ........: czardas
; Comments ......; Input is limited to +/- 9223372036854775807 for both consitency and compatibility with future libraries.
; ==============================================================================================================================
Func __WholeNumberDivision($iDividend, $iDivisor) ; Input ranges -9223372036854775807 To 9223372036854775807
If $iDivisor = 0 Then Return SetError(3, 0, $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 SetError(4, 0, $iDividend / $iDivisor) ; input range exceeded
If VarGetType($aDiv[$i]) = "Double" Then $aDiv[$i] = Number($aDiv[$i], 2) ; convert to Int-64
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 SetError(5, 0, $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
Code:
>#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