1616# See the License for the specific language governing permissions and
1717# limitations under the License.
1818# ===----------------------------------------------------------------------=== #
19- #
20- # Implements basic object methods for the Decimal type
21- # which supports correctly-rounded, fixed-point arithmetic.
22- #
23- # ===----------------------------------------------------------------------=== #
24- #
25- # Organization of files and methods of Decimal:
26- # - Internal representation fields
27- # - Constants (aliases)
28- # - Special values (methods)
29- # - Constructors and life time methods
30- # - Constructing methods that are not dunders
31- # - Output dunders, type-transfer dunders, and other type-transfer methods
32- # - Basic unary arithmetic operation dunders
33- # - Basic binary arithmetic operation dunders
34- # - Basic binary arithmetic operation dunders with reflected operands
35- # - Basic binary augmented arithmetic operation dunders
36- # - Basic comparison operation dunders
37- # - Other dunders that implements traits
38- # - Mathematical methods that do not implement a trait (not a dunder)
39- # - Other methods
40- # - Internal methods
41- #
42- # ===----------------------------------------------------------------------=== #
43- # Docstring style:
44- # 1. Description
45- # 2. Parameters
46- # 3. Args
47- # 4. Constraints
48- # 4) Returns
49- # 5) Raises
50- # 9) Examples
51- # ===----------------------------------------------------------------------=== #
5219
53- """
54- Implements basic object methods for working with decimal numbers.
20+ """ Implements basic object methods for the Decimal type.
21+
22+ This module contains the basic object methods for the Decimal type.
23+ These methods include constructors, life time methods, output dunders,
24+ type-transfer dunders, basic arithmetic operation dunders, comparison
25+ operation dunders, and other dunders that implement traits, as well as
26+ mathematical methods that do not implement a trait.
5527"""
5628
5729from memory import UnsafePointer
@@ -66,7 +38,7 @@ from decimojo.rounding_mode import RoundingMode
6638import decimojo.utility
6739
6840
69- @register_passable
41+ @register_passable ( " trivial " )
7042struct Decimal (
7143 Absable ,
7244 Comparable ,
@@ -75,11 +47,12 @@ struct Decimal(
7547 Roundable ,
7648 Writable ,
7749):
78- """
79- Correctly-rounded fixed-precision number.
50+ """ Represents a fixed-point decimal number with 96-bit precision.
51+
52+ Notes:
53+
54+ Internal Representation:
8055
81- Internal Representation
82- -----------------------
8356 Each decimal uses a 128-bit on memory, where:
8457 - 96 bits for the coefficient (significand), which is 96-bit unsigned
8558 integers stored as three 32 bit integer (little-endian).
@@ -97,12 +70,31 @@ struct Decimal(
9770 The value of the coefficient is: `high * 2**64 + mid * 2**32 + low`
9871 The final value is: `(-1)**sign * coefficient * 10**(-scale)`
9972
100- Reference
101- ---------
73+ Reference:
74+
10275 - General Decimal Arithmetic Specification Version 1.70 – 7 Apr 2009 (https://speleotrove.com/decimal/decarith.html)
10376 - https://learn.microsoft.com/en-us/dotnet/api/system.decimal.getbits?view=net-9.0&redirectedfrom=MSDN#System_Decimal_GetBits_System_Decimal_
10477 """
10578
79+ # ===------------------------------------------------------------------=== #
80+ # Organization of fields and methods:
81+ # - Internal representation fields
82+ # - Constants (aliases)
83+ # - Special values (methods)
84+ # - Constructors and life time methods
85+ # - Constructing methods that are not dunders
86+ # - Output dunders, type-transfer dunders, and other type-transfer methods
87+ # - Basic unary arithmetic operation dunders
88+ # - Basic binary arithmetic operation dunders
89+ # - Basic binary arithmetic operation dunders with reflected operands
90+ # - Basic binary augmented arithmetic operation dunders
91+ # - Basic comparison operation dunders
92+ # - Other dunders that implements traits
93+ # - Mathematical methods that do not implement a trait (not a dunder)
94+ # - Other methods
95+ # - Internal methods
96+ # ===------------------------------------------------------------------=== #
97+
10698 # Internal representation fields
10799 var low : UInt32
108100 """ Least significant 32 bits of coefficient."""
@@ -280,13 +272,6 @@ struct Decimal(
280272 except e:
281273 raise Error(" Error in `Decimal__init__()` with Float64: " , e)
282274
283- fn __copyinit__ (out self , other : Self):
284- """ Initializes a Decimal by copying another Decimal."""
285- self .low = other.low
286- self .mid = other.mid
287- self .high = other.high
288- self .flags = other.flags
289-
290275 # ===------------------------------------------------------------------=== #
291276 # Constructing methods that are not dunders
292277 # ===------------------------------------------------------------------=== #
@@ -917,45 +902,9 @@ struct Decimal(
917902
918903 fn __str__ (self ) -> String:
919904 """ Returns string representation of the Decimal.
920- Preserves trailing zeros after decimal point to match the scale .
905+ See `to_str()` for more information .
921906 """
922- # Get the coefficient as a string (absolute value)
923- var coef = String(self .coefficient())
924- var scale = self .scale()
925- var result : String
926-
927- # Handle zero as a special case
928- if coef == " 0" :
929- if scale == 0 :
930- result = " 0"
931- else :
932- result = " 0." + " 0" * scale
933-
934- # For non-zero values, format according to scale
935- elif scale == 0 :
936- # No decimal places needed
937- result = coef
938- elif scale >= len (coef):
939- # Need leading zeros after decimal point
940- result = " 0." + " 0" * (scale - len (coef)) + coef
941- else :
942- # Insert decimal point at appropriate position
943- var insert_pos = len (coef) - scale
944- result = coef[:insert_pos] + " ." + coef[insert_pos:]
945-
946- # Ensure we have exactly 'scale' digits after decimal point
947- var decimal_point_pos = result.find(" ." )
948- var current_decimals = len (result) - decimal_point_pos - 1
949-
950- if current_decimals < scale:
951- # Add trailing zeros if needed
952- result += " 0" * (scale - current_decimals)
953-
954- # Add negative sign if needed
955- if self .is_negative():
956- result = " -" + result
957-
958- return result
907+ return self .to_str()
959908
960909 fn __repr__ (self ) -> String:
961910 """ Returns a string representation of the Decimal."""
@@ -1071,6 +1020,102 @@ struct Decimal(
10711020
10721021 return res
10731022
1023+ fn to_str (self ) -> String:
1024+ """ Returns string representation of the Decimal.
1025+ Preserves trailing zeros after decimal point to match the scale.
1026+ """
1027+ # Get the coefficient as a string (absolute value)
1028+ var coef = String(self .coefficient())
1029+ var scale = self .scale()
1030+ var result : String
1031+
1032+ # Handle zero as a special case
1033+ if coef == " 0" :
1034+ if scale == 0 :
1035+ result = " 0"
1036+ else :
1037+ result = " 0." + " 0" * scale
1038+
1039+ # For non-zero values, format according to scale
1040+ elif scale == 0 :
1041+ # No decimal places needed
1042+ result = coef
1043+ elif scale >= len (coef):
1044+ # Need leading zeros after decimal point
1045+ result = " 0." + " 0" * (scale - len (coef)) + coef
1046+ else :
1047+ # Insert decimal point at appropriate position
1048+ var insert_pos = len (coef) - scale
1049+ result = coef[:insert_pos] + " ." + coef[insert_pos:]
1050+
1051+ # Ensure we have exactly 'scale' digits after decimal point
1052+ var decimal_point_pos = result.find(" ." )
1053+ var current_decimals = len (result) - decimal_point_pos - 1
1054+
1055+ if current_decimals < scale:
1056+ # Add trailing zeros if needed
1057+ result += " 0" * (scale - current_decimals)
1058+
1059+ # Add negative sign if needed
1060+ if self .is_negative():
1061+ result = " -" + result
1062+
1063+ return result
1064+
1065+ fn to_str_scientific (self ) raises -> String:
1066+ """ Returns a string representation of this Decimal in scientific notation.
1067+
1068+ Returns:
1069+ A string representation of this Decimal in scientific notation.
1070+
1071+ Raises:
1072+ Error: If significant_digits is not between 1 and 28.
1073+
1074+ Notes:
1075+
1076+ Scientific notation format: M.NNNNe±XX where:
1077+ - M is the first significant digit.
1078+ - NNNN is the remaining significant digits.
1079+ - ±XX is the exponent.
1080+ """
1081+ var scale : Int = self .scale()
1082+ var coef = self .coefficient()
1083+
1084+ # Special case: zero
1085+ if self .is_zero():
1086+ if scale == 0 :
1087+ return String(" 0" )
1088+ else :
1089+ return String(" 0E-" ) + String(" 0" ) * self .scale()
1090+
1091+ while coef % 10 == 0 :
1092+ coef = coef // 10
1093+ scale -= 1
1094+
1095+ # 0.00100: coef=100, scale=5
1096+ # => 0.001: coef=1, scale=3, ndigits_fractional_part=0
1097+ # => 1.0e-3: coef=1, exponent=-3
1098+ var ndigits_coef = decimojo.utility.number_of_digits(coef)
1099+ var ndigits_fractional_part = ndigits_coef - 1
1100+ var exponent = ndigits_fractional_part - scale
1101+
1102+ # Format in scientific notation:
1103+ # sign, first digit, decimal point, remaining digits
1104+ var coef_str = String(coef)
1105+ var result : String = String(" -" ) if self .is_negative() else String(" " )
1106+ if len (coef_str) == 1 :
1107+ result = result + coef_str + String(" .0" )
1108+ else :
1109+ result = result + coef_str[0 ] + String(" ." ) + coef_str[1 :]
1110+
1111+ # Add exponent (E+XX or E-XX)
1112+ if exponent >= 0 :
1113+ result += " E+" + String(exponent)
1114+ else :
1115+ result += " E" + String(exponent)
1116+
1117+ return result
1118+
10741119 # ===------------------------------------------------------------------=== #
10751120 # Basic unary operation dunders
10761121 # neg
@@ -1166,48 +1211,24 @@ struct Decimal(
11661211 # (+, -, *, @, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |)
11671212 # ===------------------------------------------------------------------=== #
11681213
1169- fn __radd__ (self , other : Float64) raises -> Self:
1170- try :
1171- return decimojo.arithmetics.add(Decimal(other), self )
1172- except e:
1173- raise Error(" Error in `__radd__()`: " , e)
1174-
11751214 fn __radd__ (self , other : Int) raises -> Self:
11761215 try :
11771216 return decimojo.arithmetics.add(Decimal(other), self )
11781217 except e:
11791218 raise Error(" Error in `__radd__()`: " , e)
11801219
1181- fn __rsub__ (self , other : Float64) raises -> Self:
1182- try :
1183- return decimojo.arithmetics.subtract(Decimal(other), self )
1184- except e:
1185- raise Error(" Error in `__rsub__()`: " , e)
1186-
11871220 fn __rsub__ (self , other : Int) raises -> Self:
11881221 try :
11891222 return decimojo.arithmetics.subtract(Decimal(other), self )
11901223 except e:
11911224 raise Error(" Error in `__rsub__()`: " , e)
11921225
1193- fn __rmul__ (self , other : Float64) raises -> Self:
1194- try :
1195- return decimojo.arithmetics.multiply(Decimal(other), self )
1196- except e:
1197- raise Error(" Error in `__rmul__()`: " , e)
1198-
11991226 fn __rmul__ (self , other : Int) raises -> Self:
12001227 try :
12011228 return decimojo.arithmetics.multiply(Decimal(other), self )
12021229 except e:
12031230 raise Error(" Error in `__rmul__()`: " , e)
12041231
1205- fn __rtruediv__ (self , other : Float64) raises -> Self:
1206- try :
1207- return decimojo.arithmetics.true_divide(Decimal(other), self )
1208- except e:
1209- raise Error(" Error in `__rtruediv__()`: " , e)
1210-
12111232 fn __rtruediv__ (self , other : Int) raises -> Self:
12121233 try :
12131234 return decimojo.arithmetics.true_divide(Decimal(other), self )
0 commit comments