Big integer algorithm [01] Big integer representation and related definitions, integer 01
★Definition of related data types
Before starting the business, it is necessary to define various data types to avoid confusion in future encoding.
UintX X-bit unsigned integer. For example, uint32 indicates 32-bit unsigned integer.
IntX X-bit signed integer. For example, int32 indicates 32-bit signed integer.
Basic Data Type Definition:
# Ifdef _ MSC_VER
Typedef _ int8 int8;
Typedef _ int16 int16;
Typedef _ int32;
Typedef _ int64 int64;
Typedef unsigned _ int8 uint8;
Typedef unsigned _ int16 uint16;
Typedef unsigned _ int32 uint32;
Typedef unsigned _ int64 uint64;
# Else
Typedef signed char int8;
Typedef signed short int16;
Typedef signed int int32;
Typedef signed long int64;
Typedef unsigned char uint8;
Typedef unsigned short uint16;
Typedef unsigned int uint32;
Typedef unsigned long uint64;
# Endif
The Type Definitions here may not all be used, because they are used directly from other projects.
Boolean Type:
Typedef uint8 boolean;
# Define TRUE 1
# Define FALSE 0
★Definition of big Integer type
The maximum type of C language is 64-bit. How should I save the unsigned integer that exceeds 64-bit? It's easy to use multiple single-precision Storage types.
For example, 1234 can be expressed as 1x1000 + 2x100 + 3x10 + 4x1, so it is easy to think of using decimal, each bit of the array is represented by 0 to 9, then write the addition, subtraction, multiplication, division, and Division functions for the number Array Based on the pen calculation principle. However, the efficiency is very low because the large number of 1024 bits also has more than three hundred bits in decimal format. For any operation, both require repeated loops in the array space with hundreds of elements, and require a lot of additional space to store the advance and backward signs and intermediate results of the computation. The speed can be imagined. At the same time, for some shift operations, using decimal will become very troublesome, and the speed will be greatly reduced. For programmers, binary thinking is very important, which is embodied here. For large integer calculation, we can use 2 ^ n to simplify the calculation process (2 ^ n is actually a decimal representation of binary). For 32-bit systems, n can be set to 32, so that the number of digits used to represent a large integer will be greatly reduced. For example, decimal 4294967296 can be expressed as 1, 0 (2 ^ 32 hexadecimal), 4294967297 can be expressed as 1, 1 (2 ^ 32 hexadecimal), and it is very convenient to handle shift calculation.
First, define the single precision type and double precision type:
Typedef uint32 bn_digit; // defines the big integer digital type. Generally, the length is the length of the operating system, which is equivalent to a single precision type.
Typedef uint64 bn_udbl; // defines the double-character-length unsigned integer. The 32-bit system is 64-bit, which is equivalent to a double-precision type.
The reason for this definition is to facilitate transplantation. For the 64-bit operating system bn_digit, It is a 64-bit unsigned integer (in this case, 2 ^ 64 is used ), in this case, bn_udbl is not defined.
Bignum structure definition:
Typedef struct
{
Int sign; // a symbolic identifier. 1 indicates a non-negative integer, and-1 indicates a negative integer.
Size_t alloc; // indicates the number of available digits before the increase of the array's dp size. The number of alloc must be a non-negative integer.
Size_t used; // indicates the number of BITs used by the array dp to represent a specified integer. The number of used must be a non-negative integer.
Bn_digit * dp; // the pointer dp points to the dynamically allocated array that represents the specified multi-precision integer. All the deficiencies (alloc-used) are set to 0, data in the array is stored in the LSB sequence (low address storage low)
} Bignum;
Valid bignum structure:
Considering the efficiency and code robustness, several rules are specified for the status of the bignum structure (except in some cases ):
1. The alloc value is a non-negative integer. That is to say, dp either points to an array with pre-allocated space or is NULL.
2. The used value is a non-negative integer and used <= alloc.
3. the used value implies that the number of used-1 digits in the array dp cannot be 0, that is, the high position headed by 0 must be truncated, the used and above positions in the array dp must be set to 0.
4. If used is 0, sign = 1. At this time, the value of bignum is 0 or dp is only initialized but no memory is allocated.
★Parameter transfer
During the development of any database, the conventions for passing function parameters should be determined as soon as possible to avoid difficulties in future development as the library increases and complexity increases. In my big integer library, values are assigned. For example, calculate the sum of two big integers a and B and put the result in c. The function prototype is int bn_add_bn (bignum * c, const bignum * a, const bignum * B ); c = a + B. The function returns an integer indicating whether an error is encountered during computing, for example, memory allocation failure. If 0 is returned, the function call is normal.
★Error Handling
In the big integer algorithm, the most likely problem is the dynamic memory allocation error. Of course, there are other problems that will be discussed later. If a memory error occurs, the subsequent computation cannot be performed. immediately exit and return the error value. In my large database, the following errors are defined:
// Error Value
# Define BN_MEMORY_ALLOCATE_FAIL-0x0001 // Dynamic Memory Allocation Error
# Define BN_EXCEED_MAX_LIMB-0x0002 // exceeds the maximum number of digits
# Define BN_EXCEED_MAX_BITS-0x0003 // exceeds the maximum bit
# Define BN_NEGATIVE_VALUE_ERROR-0x0004 // negative number Error
# Define BN_INVALID_INPUT-0x0005 // invalid input
# Define BN_DIVISION_BY_ZERO-0x0006 // divide by 0 Error
# Define BN_BUFFER_TOO_SMALL-0x0007 // the buffer is too small
# Define BN_READ_FILE_ERROR-0x0008 // file read error
# Define BN_WRITE_FILE_ERROR-0x0009 // file write error
# Define BN_NO_MODULAR_INVERSE-0x000A // The modulo inverse does not exist.
Error Check macro:
# Define BN_CHECK (x) if (ret = x )! = 0) {goto clean ;}
This macro is used to check the execution of each operation in the function. Once an error is detected (the return value of a non-0 function), the return value of the function is set to ret as the error value x, then jump to the end of the function and execute the cleanup operation (usually the dynamic memory release of the temporary variable ). In this macro definition, the goto statement is used. It is usually necessary to avoid using the goto statement. However, when all functions may fail to be called, it makes sense to use several lines of code to handle errors.
For a function with a large integer, the general form is as follows:
Int bn_function (bignum * B, const bignum *)
{
Int ret; // If the BN_CHECK macro is used, ret must be defined
Bignum c;
Bn_init (& c); // initialization of temporary variables
/**
* Do something here
*/
BN_CHECK (bn_function2 (B, a, c); // BN_CHECK checks the call status of bn_function2. If an error occurs, Jump directly to clean and perform the cleaning operation.
Clean:
Bn_free (& c); // the memory of the temporary variable is released.
Return ret; // return the error value. If no error exists, ret = 0.
}
★Summary
This article briefly introduces the definition of big integers. The next article will introduce the most basic maintenance algorithms, including initialization of large integers, increased precision, and memory release.