Introduction
We know that Microsoft.. NET Framework. the decimal structure (in the C # language, the medium price is the decimal keyword) is used to represent the decimal number, ranging from-(296-1) to 296-1, and can have 28 decimal places. This means:
- Decimal. minvalue =-79,228,162,514,264,337,593,543,950,335 =-(296-1)
- Decimal. maxvalue = 79,228,162,514,264,337,593,543,950,335 = 296-1
- Decimal. Epsilon = 0.0000000000000000000000000001 = 10-28
The preceding two static read-only fields are decimal. Unfortunately, the third one does not belong to the decimal structure.
Decimal uses four 32-bit system. int32 files for storage, occupying 128 bits = 16 bytes. The allocation of bits of the 128 type is as follows:
- 96 bits indicates the integer from 0 to 296-1, which is distributed in three 32-bit system. int32.
- The remaining 32-bit system. int32 includes the symbol bit and proportional factor.
- 31st bit indicates the symbol bit, 0 indicates a positive number, and 1 indicates a negative number.
- 16th to 23 bits indicate a proportional factor, which must contain an index between 0 and 28, indicating the power of 10, that is, the decimal point location, that is, the number of digits on the right of the decimal point.
- In fact, the index between 0 and 28 only needs 5 bits, while the above 16th to 23 Bit total 8 bits = 1 byte. That is to say, the remaining 3 bits (21st to 23 Bit) must be zero.
- Other BITs (0-15 bit and 24-30 bit) are not used and must be zero.
The decimal. getbits method returns the internal representation of the preceding decimal. The decimal (INT [] bits) constructor uses this internal representation to construct a decimal instance. A decimal may have several different internal representations. All these internal representations are equally valid and are equal in numerical values.
Tinydecimal Data Type
To better understand the decimal structure, we construct a tinydecimal structure with only 8 bits = 1 byte:
- Number: 0th to 5 bits (6 bits in total) indicates an integer from 0 to 26-1, with a total of 64 bits.
- Exp: 6th bits indicate a proportional factor, which contains an index between 0 and 1, indicating the power of 10, that is, the decimal point. 0 indicates that the decimal point is on the rightmost side.
- Sign: 7th bit indicates the sign bit. 0 indicates a positive number, and 1 indicates a negative number.
Therefore:
- Tinydecimal. minvalue =-63 =-(26-1)
- Tinydecimal. maxvalue = 63 = 26-1
- Tinydecimal. Epsilon = 0.1 = 10-1
That is to say, tinydecimal indicates that the range is from-63 to 63, and there can be 1 decimal.
Tinydecimal has the following two conditions:
- When exp = 1: 0.1, 0.2,..., 0.9, 1.0, 1.1,..., 6.2, 6.3. A total of 63.
- When exp = 0: 1, 2,..., 63. A total of 63, but the first 6 (1 = 1.0, 2 = 2.0,..., 6 = 6.0) and above are repeated.
Therefore, the positive number of tinydecimal is 63 + (63-6) = 120. There are also 120 negative numbers. Therefore, tinydecimal has 241 different values, that is, each of the positive and negative numbers is 120, plus a zero value. Note that zero has four different representations: + 0,-0, + 0.0,-0.0. The order of positive numbers of tinydecimal is as follows:
- 0.1, 0.2,..., 6.2, 6.3, 7, 8, 9, 10, 11,..., 62, 63
Note: In tinydecimal, the next number of 6.3 is 7, And the next number of 7 is 8, according to the absence of numbers such as 6.4 and 7.1. The following example is provided:
- 6.3 + 0.1 = 6.3
- 6.3 + 0.3 = 7
- 7 + 0.4 = 7
- 7 + 0.6 = 8
- 63 + 1 = Overflow
We know that 1 byte can represent 28 = 256 different values. Tinydecimal has 241 different values. The calculation is as follows: 241 = 256-6*2-3, that is, 6*2 positive numbers of duplicates and 3 zero duplicates must be deducted.
Test Program
The system. Decimal structure is an enlarged version of The tinydecimal structure. To better understand the above content, I wrote the following test program:
1 Using System; 2 3 Static Class Decimaltester 4 { 5 Static Void Main () 6 { 7 VaR Epsilon = 0.0000000000000000000000000001 m ; 8 VaR A = Decimal . Maxvalue/ 100 ; 9 VaR B =7.1234567890123456789012345685 m ; 10 Console. writeline ( " {0}: 1e-28 " , Epsilon ); 11 Console. writeline ( " {0 }:1e-28 + 0.1 " , 0.1 m + Epsilon ); 12 Console. writeline (" {0}: " , ); 13 Console. writeline ( " {0,-30}: A + 0.004 " , A + 0.004 m ); 14 Console. writeline ( " {0,-30}: A + 0.005 " , A + 0.005 m ); 15 Console. writeline ( " {0,-30}: A + 0.01 " , A + 0.01 m ); 16 Console. writeline ( " {0,-30}: A + 0.099 " , A + 0.099 m ); 17 Console. writeline ( " {0,-30}: A + 0.1 " , A + 0.1 m ); 18 Console. writeline ( " {0,-30}: (A + 0.1) + 1e-28 " , A + 0.1 m + Epsilon ); 19 Console. writeline ( " {0,-30}: A + (0.1 + 1e-28) " , A + (0.1 m + Epsilon )); 20 Console. writeline ( " {0}: B " , B ); 21 Console. writeline ( " {0,-30}: B + 1 " , B + 1 ); 22 } 23 }
The Epsilon of the first row of this program is the decimal. Epsilon mentioned in the Introduction. Its value is 10-28, and decimal can represent the smallest positive number.
Compile and run in Linux
Compile and run the 64-bit arch Linux mono 3.0.4 environment:
Work $DMCS -- versionMono C # compiler version 3.0.4.0work $DMCS decimaltester. CSWork $Mono decimaltester.exe0.0000000000000000000000000001: 1e-280.00000000000000000000001: 1e-28 + pushed: A + 0.1792281625142643375935439503.5: (A + 0.1) + pushed: A + (0.1 + 1e-28) 7.1234567890123456789012345685: b8.123456789012345678901234569: B + 1
The rows of the preceding running results are as follows:
- Epsilon = 10-28 = 0.0000000000000000000000000001, which is the smallest positive number that decimal can represent.
-
- Epsilon + 0.1 = 10-28 + 0.1 = 0.1000000000000000000000000001.
-
- A = decimal. Value/100 = 79... 3.35. This number has 29 valid digits.
-
- A + 0.004 = 79... 3.354, rounded to 79... 3.350Is equal to.
-
- A + 0.005 = 79... 3.355, rounded to 79... 3.360But this number cannot be expressed in decimal, so it has to be rounded to 79... 3.400The number is only 28 valid digits.
-
- A + 0.01 = 79... 3.36, as mentioned above, this number cannot be expressed in decimal, so it has to be rounded to 79... 3.40.
-
- A + 0.099 = 79... 3.449, rounded to 79... 3.400.
-
- A + 0.1 = 79... 3.45, rounded to 79... 3.50.
-
- (A + 0.1) + 10-28. The result is the same as that of the previous row.
-
- A + (0.1 + 10-28). The result is the same as that of the previous row.
- B = 7.1234567890123456789012345685. This number has 29 valid digits.
-
- B + 1 = 8. 1... 85. But this number cannot be expressed in decimal, so it has to be rounded to 8. 1... 9.0.
From the above analysis, we can see that in the mono environment of Linux, the Rounding Rule of decimal arithmetic operations is rounding.
Compile and run in Windows
Compile and run Windows 7 SP1 32-bit in Microsoft. NET Framework 4.5:
D: \ work>CSC decimalsumtester. CSMicrosoft (r) Visual C # compiler version 4.0.30319.17929 is used for Microsoft (R). Net Framework 4.5 copyright ownership (c) Microsoft Corporation. All rights reserved. D: \ work>Decimaltester0.0000000000000000000000000001: 1e-280.00000000000000000000001: 1e-28 + pushed: A + 0.1792281625142643375935439503.4: (A + 0.1) + pushed: A + (0.1 + 1e-28) 7.1234567890123456789012345685: b8.123456789012345678901234568: B + 1
The rows of the preceding running results are as follows:
-
- Epsilon = 10-28 = 0.0000000000000000000000000001, which is the smallest positive number that decimal can represent.
-
- Epsilon + 0.1 = 10-28 + 0.1 = 0.1000000000000000000000000001.
- A = decimal. Value/100 = 79... 3.35. This number has 29 valid digits.
-
- A + 0.004 = 79... 3.354, rounded to 79... 3.350Is equal to.
-
- A + 0.005 = 79... 3.355, rounded to 79... 3.360But this number cannot be expressed in decimal, so it has to be rounded to 79... 3.400The number is only 28 valid digits.
-
- A + 0.01 = 79... 3.36, as mentioned above, this number cannot be expressed in decimal, so it has to be rounded to 79... 3.40.
-
- A + 0.099 = 79... 3.449, rounded to 79... 3.400.
-
- A + 0.1 = 79... 3.45, rounded to 79... 3.40.
-
- (A + 0.1) + 10-28. The result is the same as that of the previous line, which is equal to 79... 3.4.0. Because 10-28 is too small, adding it will not change anything.
-
- A + (0.1 + 10-28) = 79... 3.4500000000000000000000000001, rounded to 79... 3.50... 0. Compared with the previous line, it is found that addition does not meet the combination law.
- B = 7.1234567890123456789012345685. This number has 29 valid digits.
-
- B + 1 = 8. 1... 85. But this number cannot be expressed in decimal, so it has to be rounded to 8. 1... 8.0.
From the above analysis, we can see that in the windows. NET Framework environment, the Rounding Rule for decimal arithmetic operations is four homes, six in five get even. Therefore, output in lines 8th, 9, and 12 is different from output in Linux.
Decimal has limited precision and can only represent a finite number of scattered values. In some special arithmetic operations, unexpected results are generated. In addition, the next loop is decomposed.
References
- Msdn: decimal structure (system)
- Msdn: decimal. getbits method (system)
- Msdn: decimal Constructor (int32 []) (system)