CInt96 - 96 bit integer data type

User projects written in or related to FreeBASIC.
Josep Roca
Posts: 564
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

CInt96 - 96 bit integer data type

Post by Josep Roca »

This is my attempt to implement a 96-bit data type for Free Basic. It uses a DECIMAL structure with a scale of 0 to store the values. It supports up to 29 significant digits and can represent values in excess of 7.9228 x 10^28. The largest possible value is +/-79,228,162,514,264,337,593,543,950,335.

Thanks to the overloading feature of Free Basic, you can use it as the other available numeric data types, e.g.

Code: Select all

DIM int96 AS CInt96 = 1234567890
int96 = int96 + 111
int96 = int96 - 111
int96 = int96 * 2
int96 = int96 / 2
int96 += 123
int96 -= 123
int96 *= 2
int96 /= 2
Because the bigger numeric variable natively supported by Free Basic is a long integer, if we want to set bigger values we need to use strings, e.g.

Code: Select all

DIM int96 AS CInt96 = "79228162514264337593543950335"
Documentation: https://github.com/JoseRoca/WinFBX/blob ... 20Class.md

BTW my WinFBX framework implements other data types such Currency, Decimal, Variant, Safe Array and Dictionary (associative arrays), but I'm posting this one here because it doesn't need the framework.

Save the following code as CInt96.bi (or CInt96.inc or whatever you like). To use it, just use #include "CInt96.bi".

Code: Select all

' ########################################################################################
' Microsoft Windows
' File: CInt.inc
' Contents: 96-bit (12 bytes) integer data type.
' Compiler: FreeBasic 32 & 64-bit
' Written in 2018 by José Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################

#pragma once
#include once "windows.bi"
#include once "win/ole2.bi"

' ========================================================================================
' CInt96 class
' ========================================================================================
TYPE CInt96

Public:
   m_int96 AS DECIMAL   ' // The underlying DECIMAL structure

Public:
   DECLARE CONSTRUCTOR
   DECLARE CONSTRUCTOR (BYREF cSrc AS CInt96)
   DECLARE CONSTRUCTOR (BYREF decSrc AS DECIMAL)
   DECLARE CONSTRUCTOR (BYVAL nInteger AS LONGINT)
   DECLARE CONSTRUCTOR (BYVAL nUInteger AS ULONGINT)
   DECLARE CONSTRUCTOR (BYREF wszSrc AS WSTRING)
   DECLARE DESTRUCTOR
   DECLARE OPERATOR LET (BYREF cSrc AS CInt96)
   DECLARE OPERATOR LET (BYVAL nInteger AS LONGINT)
   DECLARE OPERATOR LET (BYVAL nUInteger AS ULONGINT)
   DECLARE OPERATOR LET (BYREF wszSrc AS WSTRING)
   DECLARE OPERATOR += (BYREF int96 AS CInt96)
   DECLARE OPERATOR += (BYVAL nValue AS LONGINT)
   DECLARE OPERATOR += (BYVAL nValue AS ULONGINT)
   DECLARE OPERATOR += (BYREF wszValue AS WSTRING)
   DECLARE OPERATOR -= (BYREF int96 AS CInt96)
   DECLARE OPERATOR -= (BYVAL nValue AS LONGINT)
   DECLARE OPERATOR -= (BYVAL nValue AS ULONGINT)
   DECLARE OPERATOR -= (BYREF wszValue AS WSTRING)
   DECLARE OPERATOR *= (BYREF int96 AS CInt96)
   DECLARE OPERATOR *= (BYVAL nValue AS LONGINT)
   DECLARE OPERATOR *= (BYVAL nValue AS ULONGINT)
   DECLARE OPERATOR *= (BYREF wszValue AS WSTRING)
   DECLARE OPERATOR /= (BYREF cOperand AS CInt96)
   DECLARE OPERATOR /= (BYVAL nValue AS LONGINT)
   DECLARE OPERATOR /= (BYVAL nValue AS ULONGINT)
   DECLARE OPERATOR /= (BYREF wszValue AS WSTRING)
   DECLARE OPERATOR CAST () AS BYTE
   DECLARE OPERATOR CAST () AS UBYTE
   DECLARE OPERATOR CAST () AS SHORT
   DECLARE OPERATOR CAST () AS USHORT
   DECLARE OPERATOR CAST () AS LONG
   DECLARE OPERATOR CAST () AS ULONG
   DECLARE OPERATOR CAST () AS LONGINT
   DECLARE OPERATOR CAST () AS ULONGINT
   DECLARE OPERATOR CAST () AS SINGLE
   DECLARE OPERATOR CAST () AS DOUBLE
   DECLARE OPERATOR CAST () AS CURRENCY
   DECLARE OPERATOR CAST () AS VARIANT
   DECLARE OPERATOR CAST () AS STRING
   DECLARE FUNCTION Abs_ () AS CInt96
   DECLARE FUNCTION ToVar () AS VARIANT
   DECLARE FUNCTION Sign () AS UBYTE
   DECLARE FUNCTION IsSigned () AS BOOLEAN
   DECLARE FUNCTION IsUnsigned () AS BOOLEAN

END TYPE
' ========================================================================================

' ========================================================================================
' CInt96 constructors
' ========================================================================================
PRIVATE CONSTRUCTOR CInt96
END CONSTRUCTOR
' ========================================================================================
' ========================================================================================
PRIVATE CONSTRUCTOR CInt96 (BYREF cSrc AS CInt96)
   m_int96 = cSrc.m_int96
END CONSTRUCTOR
' ========================================================================================
' ========================================================================================
PRIVATE CONSTRUCTOR CInt96 (BYREF decSrc AS DECIMAL)
   IF decSrc.Scale THEN SetLastError(VarDecInt(@decSrc, @m_int96)) ELSE m_int96 = decSrc
END CONSTRUCTOR
' ========================================================================================
' ========================================================================================
PRIVATE CONSTRUCTOR CInt96 (BYVAL nInteger AS LONGINT)
   SetLastError(VarDecFromI8(nInteger, @m_int96))
END CONSTRUCTOR
' ========================================================================================
' ========================================================================================
PRIVATE CONSTRUCTOR CInt96 (BYVAL nUInteger AS ULONGINT)
   SetLastError(VarDecFromUI8(nuInteger, @m_int96))
END CONSTRUCTOR
' ========================================================================================
' ========================================================================================
PRIVATE CONSTRUCTOR CInt96 (BYREF wszSrc AS WSTRING)
   IF INSTR(wszSrc, ".") THEN SetLastError(E_INVALIDARG) : EXIT CONSTRUCTOR
   SetLastError(VarDecFromStr(@wszSrc, 0, 0, @m_int96))
END CONSTRUCTOR
' ========================================================================================

' ========================================================================================
' CInt96 destructor
' ========================================================================================
PRIVATE DESTRUCTOR CInt96
END DESTRUCTOR
' ========================================================================================

' ========================================================================================
' Assignment operators
' ========================================================================================
PRIVATE OPERATOR CInt96.LET (BYREF cSrc AS CInt96)
   m_int96 = cSrc.m_int96
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.LET (BYVAL nInteger AS LONGINT)
   SetLastError(VarDecFromI8(nInteger, @m_int96))
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.LET (BYVAL nUInteger AS ULONGINT)
   SetLastError(VarDecFromUI8(nuInteger, @m_int96))
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.LET (BYREF wszSrc AS WSTRING)
   IF INSTR(wszSrc, ".") THEN SetLastError(E_INVALIDARG) : EXIT OPERATOR
   SetLastError(VarDecFromStr(@wszSrc, 0, 0, @m_int96))
END OPERATOR
' ========================================================================================

' ========================================================================================
' Comparison operators.
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR = (BYREF int961 AS CInt96, BYREF int962 AS CInt96) AS BOOLEAN
   RETURN (VarDecCmp(@int961.m_int96, @int962.m_int96) = VARCMP_EQ)
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR <> (BYREF int961 AS CInt96, BYREF int962 AS CInt96) AS BOOLEAN
   RETURN (VarDecCmp(@int961.m_int96, @int962.m_int96) <> VARCMP_EQ)
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR < (BYREF int961 AS CInt96, BYREF int962 AS CInt96) AS BOOLEAN
   RETURN (VarDecCmp(@int961.m_int96, @int962.m_int96) = VARCMP_LT)
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR > (BYREF int961 AS CInt96, BYREF int962 AS CInt96) AS BOOLEAN
   RETURN (VarDecCmp(@int961.m_int96, @int962.m_int96) = VARCMP_GT)
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR <= (BYREF int961 AS CInt96, BYREF int962 AS CInt96) AS BOOLEAN
   DIM hr AS HRESULT = VarDecCmp(@int961.m_int96, @int962.m_int96)
   RETURN (hr = VARCMP_LT) OR (hr = VARCMP_EQ)
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR >= (BYREF int961 AS CInt96, BYREF int962 AS CInt96) AS BOOLEAN
   DIM hr AS HRESULT = VarDecCmp(@int961.m_int96, @int962.m_int96)
   RETURN (hr = VARCMP_GT) OR (hr = VARCMP_EQ)
END OPERATOR
' ========================================================================================

' ========================================================================================
' Math operators.
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR + (BYREF int961 AS CInt96, BYREF int962 AS CInt96) AS CInt96
   DIM dec AS DECIMAL
   DIM hr AS LONG = VarDecAdd(@int961.m_int96, @int962.m_int96, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int961
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR + (BYREF int96Src AS CInt96, BYREF wszValue AS WSTRING) AS CInt96
   DIM d AS DECIMAL
   DIM dec AS DECIMAL
   IF INSTR(wszValue, ".") THEN SetLastError(E_INVALIDARG) : RETURN int96Src
   DIM hr AS LONG = VarDecFromStr(@wszValue, 0, 0, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   hr = VarDecAdd(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   RETURN dec
END OPERATOR
' ========================================================================================

' ========================================================================================
' *** Ok ***
' Make sure that nValue is greater that -9223372036854775808 or it will be truncated.
' For values smaller than -9223372036854775808 use a string.
' ========================================================================================
PRIVATE OPERATOR + (BYREF int96Src AS CInt96, BYVAL nValue AS LONGINT) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromI8(nValue, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   hr = VarDecAdd(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR + (BYVAL nValue AS LONGINT, BYREF int96Src AS CInt96) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromI8(nValue, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nValue
   hr = VarDecAdd(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nValue
   RETURN dec
END OPERATOR
' ========================================================================================

' ========================================================================================
' *** Ok ***
' Make sure that nValue does not exceed 18446744073709551615 or it will be truncated.
' For values greater than 18446744073709551615 use a string.
' ========================================================================================
PRIVATE OPERATOR + (BYREF int96Src AS CInt96, BYVAL nValue AS ULONGINT) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromUI8(nValue, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   hr = VarDecAdd(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR + (BYVAL nValue AS ULONGINT, BYREF int96Src AS CInt96) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromUI8(nValue, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nValue
   hr = VarDecAdd(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nValue
   RETURN dec
END OPERATOR
' ========================================================================================

' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR CInt96.+= (BYREF int96Src AS CInt96)
    SetLastError(VarDecAdd(@m_int96, @int96Src.m_int96, @m_int96))
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
' Make sure that nValue is greater that -9223372036854775808 or it will be truncated.
' For values smaller than -9223372036854775808 use a string.
PRIVATE OPERATOR CInt96.+= (BYVAL nValue AS LONGINT)
   DIM dec AS DECIMAL
   DIM hr AS LONG = VarDecFromI8(nValue, @dec)
   IF hr <> S_OK THEN SetLastError(hr) ELSE SetLastError(VarDecAdd(@m_int96, @dec, @m_int96))
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
' Make sure that nValue does not exceed 18446744073709551615 or it will be truncated.
' For values greater than 18446744073709551615 use a string.
PRIVATE OPERATOR CInt96.+= (BYVAL nValue AS ULONGINT)
   DIM dec AS DECIMAL
   DIM hr AS LONG = VarDecFromUI8(nValue, @dec)
   IF hr <> S_OK THEN SetLastError(hr) ELSE SetLastError(VarDecAdd(@m_int96, @dec, @m_int96))
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR CInt96.+= (BYREF wszValue AS WSTRING)
   DIM d AS DECIMAL
   DIM dec AS DECIMAL
   IF INSTR(wszValue, ".") THEN SetLastError(E_INVALIDARG) : EXIT OPERATOR
   DIM hr AS LONG = VarDecFromStr(@wszValue, 0, 0, @d)
   IF hr <> S_OK THEN SetLastError(hr) ELSE SetLastError(VarDecAdd(@m_int96, @d, @m_int96))
END OPERATOR
' ========================================================================================

' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR - (BYREF int961 AS CInt96, BYREF int962 AS CInt96) AS CInt96
   DIM dec AS DECIMAL
   DIM hr AS LONG = VarDecSub(@int961.m_int96, @int962.m_int96, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int961
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
' Make sure that nValue is greater that -9223372036854775808 or it will be truncated.
' For values smaller than -9223372036854775808 use a string.
PRIVATE OPERATOR - (BYREF int96Src AS CInt96, BYVAL nValue AS LONGINT) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromI8(nValue, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   hr = VarDecSub(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
' Make sure that nValue does not exceed 18446744073709551615 or it will be truncated.
' For values greater than 18446744073709551615 use a string.
PRIVATE OPERATOR - (BYREF int96Src AS CInt96, BYVAL nValue AS ULONGINT) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromUI8(nValue, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   hr = VarDecSub(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
' Make sure that nValue is greater that -9223372036854775808 or it will be truncated.
' For values smaller than -9223372036854775808 use a string.
PRIVATE OPERATOR - (BYVAL nValue AS LONGINT, BYREF int96Src AS CInt96) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromI8(nValue, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nValue
   hr = VarDecSub(@d, @int96Src.m_int96, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nValue
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
' Make sure that nValue does not exceed 18446744073709551615 or it will be truncated.
' For values greater than 18446744073709551615 use a string.
PRIVATE OPERATOR - (BYVAL nValue AS ULONGINT, BYREF int96Src AS CInt96) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromUI8(nValue, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nValue
   hr = VarDecSub(@d, @int96Src.m_int96, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nValue
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR - (BYREF int96Src AS CInt96, BYREF wszValue AS WSTRING) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   IF INSTR(wszValue, ".") THEN SetLastError(E_INVALIDARG) : RETURN int96Src
   DIM hr AS LONG = VarDecFromStr(@wszValue, 0, 0, @d)
   IF VarDecSub(@int96Src.m_int96, @d, @dec) <> S_OK THEN RETURN int96Src
   RETURN dec
END OPERATOR
' ========================================================================================

' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR CInt96.-= (BYREF int96Src AS CInt96)
   SetLastError(VarDecSub(@m_int96, @int96Src.m_int96, @m_int96))
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
' Make sure that nValue does not exceed 18446744073709551615 or it will be truncated.
' For values greater than 18446744073709551615 use a string.
PRIVATE OPERATOR CInt96.-= (BYVAL nValue AS LONGINT)
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromI8(nValue, @d)
   IF hr <> S_OK THEN SetLastError(hr) ELSE SetLastError(VarDecSub(@m_int96, @d, @m_int96))
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
' Make sure that nValue does not exceed 18446744073709551615 or it will be truncated.
' For values greater than 18446744073709551615 use a string.
PRIVATE OPERATOR CInt96.-= (BYVAL nValue AS ULONGINT)
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromUI8(nValue, @d)
   IF hr <> S_OK THEN SetLastError(hr) ELSE SetLastError(VarDecSub(@m_int96, @d, @m_int96))
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR CInt96.-= (BYREF wszValue AS WSTRING)
   DIM d AS DECIMAL
   DIM dec AS DECIMAL
   IF INSTR(wszValue, ".") THEN EXIT OPERATOR
   DIM hr AS LONG = VarDecFromStr(@wszValue, 0, 0, @d)
   IF hr <> S_OK THEN SetLastError(hr) ELSE SetLastError(VarDecSub(@m_int96, @d, @m_int96))
END OPERATOR
' ========================================================================================

' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR - (BYREF int96Src AS CInt96) AS CInt96
   DIM dec AS DECIMAL
   SetLastError(VarDecNeg(@int96Src.m_int96, @dec))
   RETURN dec
END OPERATOR
' ========================================================================================

' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR * (BYREF int961 AS CInt96, BYREF int962 AS CInt96) AS CInt96
   DIM dec AS DECIMAL
   DIM hr AS LONG = VarDecMul(@int961.m_int96, @int962.m_int96, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int961
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR * (BYVAL nOperand AS LONGINT, BYREF int96Src AS CInt96) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromI8(nOperand, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nOperand
   hr = VarDecMul(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nOperand
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR * (BYREF int96Src AS CInt96, BYVAL nOperand AS LONGINT) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromI8(nOperand, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   hr = VarDecMul(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR * (BYREF int96Src AS CInt96, BYVAL nOperand AS ULONGINT) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr  AS LONG = VarDecFromUI8(nOperand, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   hr = VarDecMul(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR * (BYVAL nOperand AS ULONGINT, BYREF int96Src AS CInt96) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromUI8(nOperand, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nOperand
   hr = VarDecMul(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nOperand
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR * (BYREF int96Src AS CInt96, BYREF wszValue AS WSTRING) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   IF INSTR(wszValue, ".") THEN SetLastError(E_INVALIDARG) : RETURN int96Src
   DIM hr AS LONG = VarDecFromStr(@wszValue, 0, 0, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   hr = VarDecMul(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   RETURN dec
END OPERATOR
' ========================================================================================

' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR CInt96.*= (BYREF int96Src AS CInt96)
   SetLasterror(VarDecMul(@m_int96, @int96Src.m_int96, @m_int96))
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR CInt96.*= (BYVAL nOperand AS LONGINT)
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromI8(nOperand, @d)
   IF hr THEN SetLastError(hr) ELSE SetLastError(VarDecMul(@m_int96, @d, @m_int96))
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR CInt96.*= (BYVAL nOperand AS ULONGINT)
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromUI8(nOperand, @d)
   IF hr <> S_OK THEN SetLasterror(hr) ELSE SetLastError(VarDecMul(@m_int96, @d, @m_int96))
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR CInt96.*= (BYREF wszValue AS WSTRING)
   DIM d AS DECIMAL
   DIM dec AS DECIMAL
   IF INSTR(wszValue, ".") THEN SetLastError(E_INVALIDARG) : EXIT OPERATOR
   DIM hr AS LONG = VarDecFromStr(@wszValue, 0, 0, @d)
   IF hr <> S_OK THEN SetLastError(hr) ELSE SetLastError(VarDecMul(@m_int96, @d, @m_int96))
END OPERATOR
' ========================================================================================

' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR / (BYREF int96Src AS CInt96, BYREF cOperand AS CInt96) AS CInt96
   DIM dec AS DECIMAL
   DIM hr AS LONG = VarDecDiv(@int96Src.m_int96, @cOperand.m_int96, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   IF dec.Scale THEN hr = VarDecInt(@dec, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR / (BYREF int96Src AS CInt96, BYVAL nOperand AS LONGINT) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromI8(nOperand, @d)
   hr = VarDecDiv(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   IF dec.Scale THEN hr = VarDecInt(@dec, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR / (BYVAL nOperand AS LONGINT, BYREF int96Src AS CInt96) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromI8(nOperand, @d)
   hr = VarDecDiv(@d, @int96Src.m_int96, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nOperand
   IF dec.Scale THEN hr = VarDecInt(@dec, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   RETURN dec
END OPERATOR
' ========================================================================================

' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR / (BYREF int96Src AS CInt96, BYVAL nOperand AS ULONGINT) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromUI8(nOperand, @d)
   hr = VarDecDiv(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   IF dec.Scale THEN hr = VarDecInt(@dec, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR / (BYVAL nOperand AS ULONGINT, BYREF int96Src AS CInt96) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromUI8(nOperand, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nOperand
   hr = VarDecDiv(@d, @int96Src.m_int96, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nOperand
   IF dec.Scale THEN hr = VarDecInt(@dec, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN nOperand
   RETURN dec
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR / (BYREF int96Src AS CInt96, BYREF wszValue AS WSTRING) AS CInt96
   DIM dec AS DECIMAL
   DIM d AS DECIMAL
   IF INSTR(wszValue, ".") THEN SetLastError(E_INVALIDARG) : RETURN int96Src
   DIM hr AS LONG = VarDecFromStr(@wszValue, 0, 0, @d)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   hr = VarDecDiv(@int96Src.m_int96, @d, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   IF dec.Scale THEN hr = VarDecInt(@dec, @dec)
   IF hr <> S_OK THEN SetLastError(hr) : RETURN int96Src
   RETURN dec
END OPERATOR
' ========================================================================================

' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR CInt96./= (BYREF cOperand AS CInt96)
   DIM hr AS LONG = VarDecDiv(@m_int96, @cOperand.m_int96, @m_int96)
   IF hr <> S_OK THEN SetLastError(hr) : EXIT OPERATOR
   IF m_int96.Scale THEN SetLastError(VarDecInt(@m_int96, @m_int96))
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR CInt96./= (BYVAL nOperand AS LONGINT)
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromI8(nOperand, @d)
   IF hr <> S_OK THEN SetLastError(hr) : EXIT OPERATOR
   hr = VarDecDiv(@m_int96, @d, @m_int96)
   IF hr <> S_OK THEN SetLastError(hr) : EXIT OPERATOR
   IF m_int96.Scale THEN SetLastError(VarDecInt(@m_int96, @m_int96))
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR CInt96./= (BYVAL nOperand AS ULONGINT)
   DIM d AS DECIMAL
   DIM hr AS LONG = VarDecFromUI8(nOperand, @d)
   IF hr <> S_OK THEN SetLastError(hr) : EXIT OPERATOR
   hr = VarDecDiv(@m_int96, @d, @m_int96)
   IF hr <> S_OK THEN SetLastError(hr) : EXIT OPERATOR
   IF m_int96.Scale THEN SetLastError(VarDecInt(@m_int96, @m_int96))
END OPERATOR
' ========================================================================================
' ========================================================================================
' *** Ok ***
PRIVATE OPERATOR CInt96./= (BYREF wszValue AS WSTRING)
   DIM d AS DECIMAL
   DIM dec AS DECIMAL
   IF INSTR(wszValue, ".") THEN SetLastError(E_INVALIDARG) : EXIT OPERATOR
   DIM hr AS LONG = VarDecFromStr(@wszValue, 0, 0, @d)
   IF hr <> S_OK THEN SetLastError(hr) : EXIT OPERATOR
   hr = VarDecDiv(@m_int96, @d, @m_int96)
   IF hr <> S_OK THEN SetLastError(hr) : EXIT OPERATOR
   IF m_int96.Scale THEN SetLastError(VarDecInt(@m_int96, @m_int96))
END OPERATOR
' ========================================================================================

' ========================================================================================
' Cast operators.
' ========================================================================================
PRIVATE OPERATOR CInt96.CAST () AS BYTE
   DIM nValue AS BYTE
   SetLastError(VarI1FromDec(@m_int96, @nValue))
   OPERATOR = nValue
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.CAST () AS UBYTE
   DIM nValue AS UBYTE
   SetLastError(VarUI1FromDec(@m_int96, @nValue))
   OPERATOR = nValue
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.CAST () AS SHORT
   DIM nValue AS SHORT
   SetLastError(VarI2FromDec(@m_int96, @nValue))
   OPERATOR = nValue
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.CAST () AS USHORT
   DIM nValue AS USHORT
   SetLastError(VarUI2FromDec(@m_int96, @nValue))
   OPERATOR = nValue
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.CAST () AS LONG
   DIM nValue AS LONG
   SetLastError(VarI4FromDec(@m_int96, @nValue))
   OPERATOR = nValue
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.CAST () AS ULONG
   DIM nValue AS ULONG
   SetLastError(VarUI4FromDec(@m_int96, @nValue))
   OPERATOR = nValue
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.CAST () AS LONGINT
   DIM nValue AS LONGINT
   SetLastError(VarI8FromDec(@m_int96, @nValue))
   OPERATOR = nValue
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.CAST () AS ULONGINT
   DIM nValue AS LONGINT
   SetLastError(VarUI8FromDec(@m_int96, @nValue))
   OPERATOR = nValue
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.CAST () AS SINGLE
   DIM nValue AS SINGLE
   SetLastError(VarR4FromDec(@m_int96, @nValue))
   OPERATOR = nValue
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.CAST () AS DOUBLE
   DIM nValue AS DOUBLE
   SetLastError(VarR8FromDec(@m_int96, @nValue))
   OPERATOR = nValue
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.CAST () AS CURRENCY
   DIM cy AS CURRENCY
   SetLastError(VarCyFromDec(@m_int96, @cy))
   OPERATOR = cy
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.CAST () AS VARIANT
   DIM v AS VARIANT
   v.vt = VT_DECIMAL
   v.decVal = m_int96
   RETURN v
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CInt96.CAST () AS STRING
   DIM bstrOut AS BSTR
   VarBstrFromDec(@m_int96, 0, 0, @bstrOut)
   DIM s AS STRING = *cast(WSTRING PTR, bstrOut)
   SysFreeString(bstrOut)
   OPERATOR = s
END OPERATOR
' ========================================================================================

' ========================================================================================
' Returns the address of the underlying decimal value.
' ========================================================================================
PRIVATE OPERATOR * (BYREF int96 AS CInt96) AS DECIMAL PTR
   OPERATOR = VARPTR(int96.m_int96)
END OPERATOR
' ========================================================================================

' ========================================================================================
' Retrieves the absolute value of a CInt96 data type.
' ========================================================================================
PRIVATE FUNCTION CInt96.Abs_ () AS CInt96
   DIM dec AS DECIMAL
   SetLastError(VarDecAbs(@m_int96, @dec))
   RETURN dec
END FUNCTION
' ========================================================================================

' ========================================================================================
' Converts the currency to a VT_CY variant.
' To allow to assign the decimal to a CVAR variable
' DIM dec AS CInt96 = 12345.1234
' DIM cv AS CVAR = dec.ToVar
' ========================================================================================
PRIVATE FUNCTION CInt96.ToVar () AS VARIANT
   DIM v AS VARIANT
   v.vt = VT_DECIMAL
   v.decVal = m_int96
   RETURN v
END FUNCTION
' ========================================================================================

' ========================================================================================
' Returns the sign of this decimal number.
' Returns 0 if it is not signed of &h80 (128) if it is signed.
' ========================================================================================
PRIVATE FUNCTION CInt96.Sign () AS UBYTE
   RETURN m_int96.sign
END FUNCTION
' ========================================================================================
' ========================================================================================
' Returns true if this number is signed or false otherwise.
' ========================================================================================
PRIVATE FUNCTION CInt96.IsSigned () AS BOOLEAN
   IF m_int96.sign THEN RETURN TRUE ELSE RETURN FALSE
END FUNCTION
' ========================================================================================
' ========================================================================================
' Returns true if this number is unsigned or false otherwise.
' ========================================================================================
PRIVATE FUNCTION CInt96.IsUnsigned () AS BOOLEAN
   IF m_int96.sign = 0 THEN RETURN TRUE ELSE RETURN FALSE
END FUNCTION
' ========================================================================================
Last edited by Josep Roca on Sep 18, 2018 4:45, edited 1 time in total.
badidea
Posts: 2594
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: CInt96 - 96 bit integer dta type

Post by badidea »

Why 96 bit and not 128 bit?
For scientific calculations the maximum is a bit limited. It can contain avogrado's constant, but not the mass of the sun in kg.
Josep Roca
Posts: 564
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: CInt96 - 96 bit integer dta type

Post by Josep Roca »

Because the Windows API functions that I have used don't allow more. The DECIMAL structure uses 12 bytes to store the number, an UBYTE for the sign and another UBYTE for the scale.

Code: Select all

type tagDEC
	wReserved as USHORT

	union
		type
			scale as UBYTE
			sign as UBYTE
		end type

		signscale as USHORT
	end union

	Hi32 as ULONG

	union
		type
			Lo32 as ULONG
			Mid32 as ULONG
		end type

		Lo64 as ULONGLONG
	end union
end type

type DECIMAL as tagDEC
deltarho[1859]
Posts: 4315
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: CInt96 - 96 bit integer dta type

Post by deltarho[1859] »

Very impressive.

Code: Select all

#Include "cint.bas"
Dim big as Cint96

big = 9223372036854775807 ' Max Longint value
big *= 7
Print big
Sleep

' Gives 64563604257983430649
José are these early days for this project? I was thinking of the bitwise operations, shr and so on.
Josep Roca
Posts: 564
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: CInt96 - 96 bit integer dta type

Post by Josep Roca »

I have implemented all I can. If somebody is able to overload the bitwise operators to work with these big numbers it will be a very welcome addition. Arithmetic shift is no my speciality.
deltarho[1859]
Posts: 4315
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: CInt96 - 96 bit integer dta type

Post by deltarho[1859] »

Josep Roca wrote:it will be a very welcome addition
It would indeed. Any takers?
Josep Roca
Posts: 564
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: CInt96 - 96 bit integer dta type

Post by Josep Roca »

I will experiment to see if I can implement ^, AND, OR, XOR, EQV, IMP and MOD. I have had a crazy idea that maybe will work. SHR and SHL are beyond my knowledge.
srvaldez
Posts: 3383
Joined: Sep 25, 2005 21:54

Re: CInt96 - 96 bit integer dta type

Post by srvaldez »

for anyone interested, the Decimal type is defined in wtypes.bi, for convenience I paste the definition below

Code: Select all

type tagDEC
	wReserved as USHORT

	union
		type
			scale as UBYTE
			sign as UBYTE
		end type

		signscale as USHORT
	end union

	Hi32 as ULONG

	union
		type
			Lo32 as ULONG
			Mid32 as ULONG
		end type

		Lo64 as ULONGLONG
	end union
end type

type DECIMAL as tagDEC
type LongLong is 64-bit or LongInt
so if you have

Code: Select all

#Include "cint.bas"
Dim big as Cint96

big = 9223372036854775807 ' Max Longint value
big *= 7
Print big
print "big.scale = ";big.m_int96.scale		''Ubyte
print "big.sign = ";big.m_int96.sign			''Ubyte
print "big.signscale = ";big.m_int96.signscale	''Ushort
print "big.Hi32 = ";big.m_int96.Hi32			''Ulong
print "big.Mid32 = ";big.m_int96.Mid32		''Ulong
print "big.Lo64 = ";big.m_int96.Lo64			''UlongInt
Sleep
knowing how to access the Decimal components, it should be straightforward to implement the desired bit functions
deltarho[1859]
Posts: 4315
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: CInt96 - 96 bit integer dta type

Post by deltarho[1859] »

@srvaldez

I was going to put you forward as one of the takers but thought would be a bit of a cheek. <smile>
srvaldez
Posts: 3383
Joined: Sep 25, 2005 21:54

Re: CInt96 - 96 bit integer dta type

Post by srvaldez »

@deltarho[1859]
to shift a dec value by 1 should be straightforward, it would be something like this
not tested

Code: Select all

Dim as Cint96 big, tmp
Dim carry as ulong

carry=bit( big.m_int96.Lo64, 63 )
tmp.m_int96.Lo64 = big.m_int96.Lo64 shl 1
tmp.m_int96.Hi32 = (big.m_int96.Hi32 shl 1) or carry
I am not sure that's right but it should be something like that, it's of course more complicated if you want to shift by more than 1
to shift right, you do something like the following
not tested

Code: Select all

Dim as Cint96 big, tmp
Dim carry as ulong

carry=bit( big.Hi32, 0 )
tmp.Hi32 = big.m_int96.Hi32 shr 1
tmp.m_int96.Lo64  = big.m_int96.Lo64  shr 1
if carry then
   tmp.m_int96.Lo64 or=&h8000000000000000ULL
end if
deltarho[1859]
Posts: 4315
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: CInt96 - 96 bit integer dta type

Post by deltarho[1859] »

Hi32 is not mentioned in Cint.
Josep Roca
Posts: 564
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: CInt96 - 96 bit integer dta type

Post by Josep Roca »

Hi32 is not a datatype but the name of a member of the DECIMAL structure and is an ULONG.

BTW my crazy idea does not work. The API functions involved overflow with these big numbers.
srvaldez
Posts: 3383
Joined: Sep 25, 2005 21:54

Re: CInt96 - 96 bit integer dta type

Post by srvaldez »

with the help of stack overflow here's the shift left function, it certainly needs to be optimized but am under the weather and not up to par
the right shift function should be similar
https://stackoverflow.com/questions/599 ... bit-number

Code: Select all

function shift_left96(byval n as CInt96, byval k as long) as CInt96
	dim as ulong a, b, c
	a=n.m_int96.Hi32
	b=n.m_int96.Mid32
	c=(n.m_int96.Lo64 shl 32 ) shr 32
	if k<=96 then
		if k>=32 then
			a=b
			b=c
			c=0
			n.m_int96.Hi32=a
			n.m_int96.Lo64=c
			n.m_int96.Mid32=b
			return shift_left96(n,k-32)
		else
			if k=0 then
				return n
			else
				a=(a shl k) or (b shr (32 - k))
				b = (b shl k) or (c shr (32 - k))
				c = c shl k
				
				n.m_int96.Hi32=a
				n.m_int96.Lo64=c
				n.m_int96.Mid32=b
			end if
		end if
	end if
	return n
end function
test

Code: Select all

#include "CInt96.inc"
DIM AS CInt96 big=8
for k as long=1 to 92
	print  k,shift_left96( big,k)
next
shift right

Code: Select all


function shift_right96(byval n as CInt96, byval k as long) as CInt96
	dim as ulong a, b, c
	a=n.m_int96.Hi32
	b=n.m_int96.Mid32
	c=(n.m_int96.Lo64 shl 32 ) shr 32
	if k<=96 then
		if k>=32 then
			c = b
			b = a
			a = 0
			n.m_int96.Hi32=a
			n.m_int96.Lo64=c
			n.m_int96.Mid32=b
			return shift_right96(n,k-32)
		else
			if k=0 then
				return n
			else
				c = (b shl (32-k)) or (c shr k)
				b = (a shl (32-k)) or (b shr k)
				a = a shr k
				
				n.m_int96.Hi32=a
				n.m_int96.Lo64=c
				n.m_int96.Mid32=b
			end if
		end if
	end if
	return n
end function
fxm
Moderator
Posts: 12158
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: CInt96 - 96 bit integer dta type

Post by fxm »

About your previous remark but now suppressed (code works with "Return' but not with 'Function ='):
- 'Return' calls the copy-constructor while 'Function =' calls the let-operator but as the two procedures have the same code, so the problem is not coming for here!
- The other difference between 'Return' and 'Function' is that 'Return' exits the procedure but not 'Function ='.
Therefore you must add 'exit function' after the first two 'Function =' (the last being the final instruction):

Code: Select all

function shift_left96(byval n as CInt96, byval k as long) as CInt96
   dim as ulong a, b, c
   a=n.m_int96.Hi32
   b=n.m_int96.Mid32
   c=(n.m_int96.Lo64 shl 32 ) shr 32
   if k<=96 then
      if k>=32 then
         a=b
         b=c
         c=0
         n.m_int96.Hi32=a
         n.m_int96.Lo64=c
         n.m_int96.Mid32=b
         function = shift_left96(n,k-32)
         exit function
      else
         if k=0 then
            function = n
            exit function
         else
            a=(a shl k) or (b shr (32 - k))
            b = (b shl k) or (c shr (32 - k))
            c = c shl k
            
            n.m_int96.Hi32=a
            n.m_int96.Lo64=c
            n.m_int96.Mid32=b
         end if
      end if
   end if
   function = n
end function
srvaldez
Posts: 3383
Joined: Sep 25, 2005 21:54

Re: CInt96 - 96 bit integer dta type

Post by srvaldez »

thank you fxm for the info, much appreciated :-)
like I mentioned, am not feeling well and my poor brain is barely functioning hence my coding blunders.
Post Reply