diff --git a/README.md b/README.md index 8421e7a..56d59dd 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,67 @@ For the latest development version, clone the [GitHub repository](https://github ## Quick start +Here are some examples showcasing the arbitrary-precision feature of the `BigDecimal` type. + +```mojo +from decimojo import BDec, RM + + +fn main() raises: + var PRECISION = 100 + var a = BDec("123456789.123456789") + var b = BDec("1234.56789") + print(a.sqrt(precision=PRECISION)) + # 11111.11106611111096943055498174930232833813065468909453818857935956641682120364106016272519460988485 + print(a.power(b, precision=PRECISION)) + # 3.346361102419080234023813540078946868219632448203078657310495672766009862564151996325555496759911131748170844123475135377098326591508239654961E+9989 + print(a.log(b, precision=PRECISION)) + # 2.617330026656548299907884356415293977170848626010103229392408225981962436022623783231699264341492663671325580092077394824180414301026578169909 +``` + +Here is a comprehensive quick-start guide showcasing each major function of the `BigInt` type. + +```mojo +from decimojo import BigInt, BInt +# BInt is an alias for BigInt + +fn main() raises: + # === Construction === + var a = BigInt("12345678901234567890") # From string + var b = BInt(12345) # From integer + + # === Basic Arithmetic === + print(a + b) # Addition: 12345678901234580235 + print(a - b) # Subtraction: 12345678901234555545 + print(a * b) # Multiplication: 152415787814108380241050 + + # === Division Operations === + print(a // b) # Floor division: 999650944609516 + print(a.truncate_divide(b)) # Truncate division: 999650944609516 + print(a % b) # Modulo: 9615 + + # === Power Operation === + print(BInt(2).power(10)) # Power: 1024 + print(BInt(2) ** 10) # Power (using ** operator): 1024 + + # === Comparison === + print(a > b) # Greater than: True + print(a == BInt("12345678901234567890")) # Equality: True + print(a.is_zero()) # Check for zero: False + + # === Type Conversions === + print(a.to_str()) # To string: "12345678901234567890" + + # === Sign Handling === + print(-a) # Negation: -12345678901234567890 + print(abs(BInt("-12345678901234567890"))) # Absolute value: 12345678901234567890 + print(a.is_negative()) # Check if negative: False + + # === Extremely large numbers === + # 3600 digits // 1800 digits + print(BInt("123456789" * 400) // BInt("987654321" * 200)) +``` + Here is a comprehensive quick-start guide showcasing each major function of the `Decimal` type. ```mojo @@ -118,67 +179,6 @@ fn main() raises: [Click here for 8 key examples](https://zhuyuhao.com/decimojo/docs/examples) highlighting the most important features of the `Decimal` type. -Here are some examples showcasing the arbitrary-precision feature of the `BigDecimal` type. - -```mojo -from decimojo import BDec, RM - - -fn main() raises: - var PRECISION = 100 - var a = BDec("123456789.123456789") - var b = BDec("1234.56789") - print(a.sqrt(precision=PRECISION)) - # 11111.11106611111096943055498174930232833813065468909453818857935956641682120364106016272519460988485 - print(a.power(b, precision=PRECISION)) - # 3.346361102419080234023813540078946868219632448203078657310495672766009862564151996325555496759911131748170844123475135377098326591508239654961E+9989 - print(a.log(b, precision=PRECISION)) - # 2.617330026656548299907884356415293977170848626010103229392408225981962436022623783231699264341492663671325580092077394824180414301026578169909 -``` - -Here is a comprehensive quick-start guide showcasing each major function of the `BigInt` type. - -```mojo -from decimojo import BigInt, BInt -# BInt is an alias for BigInt - -fn main() raises: - # === Construction === - var a = BigInt("12345678901234567890") # From string - var b = BInt(12345) # From integer - - # === Basic Arithmetic === - print(a + b) # Addition: 12345678901234580235 - print(a - b) # Subtraction: 12345678901234555545 - print(a * b) # Multiplication: 152415787814108380241050 - - # === Division Operations === - print(a // b) # Floor division: 999650944609516 - print(a.truncate_divide(b)) # Truncate division: 999650944609516 - print(a % b) # Modulo: 9615 - - # === Power Operation === - print(BInt(2).power(10)) # Power: 1024 - print(BInt(2) ** 10) # Power (using ** operator): 1024 - - # === Comparison === - print(a > b) # Greater than: True - print(a == BInt("12345678901234567890")) # Equality: True - print(a.is_zero()) # Check for zero: False - - # === Type Conversions === - print(a.to_str()) # To string: "12345678901234567890" - - # === Sign Handling === - print(-a) # Negation: -12345678901234567890 - print(abs(BInt("-12345678901234567890"))) # Absolute value: 12345678901234567890 - print(a.is_negative()) # Check if negative: False - - # === Extremely large numbers === - # 3600 digits // 1800 digits - print(BInt("123456789" * 400) // BInt("987654321" * 200)) -``` - ## Objective Financial calculations and data analysis require precise decimal arithmetic that floating-point numbers cannot reliably provide. As someone working in finance and credit risk model validation, I needed a dependable correctly-rounded, fixed-precision numeric type when migrating my personal projects from Python to Mojo. diff --git a/src/decimojo/bigdecimal/bigdecimal.mojo b/src/decimojo/bigdecimal/bigdecimal.mojo index fed87c2..3d63926 100644 --- a/src/decimojo/bigdecimal/bigdecimal.mojo +++ b/src/decimojo/bigdecimal/bigdecimal.mojo @@ -82,24 +82,47 @@ struct BigDecimal( # Constructors and life time dunder methods # ===------------------------------------------------------------------=== # + @implicit + fn __init__(out self, coefficient: BigUInt): + """Constructs a BigDecimal from a BigUInt object.""" + self.coefficient = coefficient + self.scale = 0 + self.sign = False + fn __init__(out self, coefficient: BigUInt, scale: Int, sign: Bool): """Constructs a BigDecimal from its components.""" self.coefficient = coefficient self.scale = scale self.sign = sign + @implicit + fn __init__(out self, value: BigInt): + """Constructs a BigDecimal from a big interger.""" + self.coefficient = value.magnitude + self.scale = 0 + self.sign = value.sign + fn __init__(out self, value: String) raises: """Constructs a BigDecimal from a string representation.""" # The string is normalized with `deciomojo.str.parse_numeric_string()`. self = Self.from_string(value) - fn __init__(out self, value: Int) raises: - """Constructs a BigDecimal from an integer.""" + @implicit + fn __init__(out self, value: Int): + """Constructs a BigDecimal from an `Int` object. + See `from_int()` for more information. + """ self = Self.from_int(value) - fn __init__(out self, value: Scalar) raises: - """Constructs a BigDecimal from a Mojo Scalar.""" - self = Self.from_scalar(value) + @implicit + fn __init__(out self, value: Scalar): + """Constructs a BigDecimal from an integral scalar. + This includes all SIMD integral types, such as Int8, Int16, UInt32, etc. + + Constraints: + The dtype of the scalar must be integral. + """ + self = Self.from_integral_scalar(value) # ===------------------------------------------------------------------=== # # Constructing methods that are not dunders @@ -123,10 +146,10 @@ struct BigDecimal( return Self(BigUInt(List[UInt32](coefficient)), scale, sign) @staticmethod - fn from_int(value: Int) raises -> Self: + fn from_int(value: Int) -> Self: """Creates a BigDecimal from an integer.""" if value == 0: - return Self(coefficient=BigUInt(UInt32(0)), scale=0, sign=False) + return Self(coefficient=BigUInt.ZERO, scale=0, sign=False) var words = List[UInt32](capacity=2) var sign: Bool @@ -157,8 +180,31 @@ struct BigDecimal( return Self(coefficient=BigUInt(words^), scale=0, sign=sign) @staticmethod - fn from_scalar[dtype: DType, //](value: Scalar[dtype]) raises -> Self: - """Initializes a BigDecimal from a Mojo Scalar. + fn from_integral_scalar[dtype: DType, //](value: SIMD[dtype, 1]) -> Self: + """Initializes a BigDecimal from an integral scalar. + This includes all SIMD integral types, such as Int8, Int16, UInt32, etc. + + Args: + value: The Scalar value to be converted to BigDecimal. + + Returns: + The BigDecimal representation of the Scalar value. + """ + + constrained[dtype.is_integral(), "dtype must be integral."]() + + if value == 0: + return Self(coefficient=BigUInt.ZERO, scale=0, sign=False) + + return Self( + coefficient=BigUInt.from_absolute_integral_scalar(value), + scale=0, + sign=True if value < 0 else False, + ) + + @staticmethod + fn from_float[dtype: DType, //](value: Scalar[dtype]) raises -> Self: + """Initializes a BigDecimal from a floating-point scalar. Args: value: The Scalar value to be converted to BigDecimal. @@ -171,78 +217,24 @@ struct BigDecimal( If the value is a floating-point number, it is converted to a string with full precision before converting to BigDecimal. """ - var sign = True if value < 0 else False - - @parameter - if dtype.is_integral(): - var list_of_words = List[UInt32]() - var remainder: Scalar[dtype] = value - var quotient: Scalar[dtype] - var is_min = False - - if sign: - var min_value: Scalar[dtype] - var max_value: Scalar[dtype] - - # TODO: Currently Int256 is not supported due to the limitation - # of Mojo's standard library. The following part can be removed - # if `mojo/stdlib/src/utils/numerics.mojo` is updated. - @parameter - if dtype == DType.int128: - min_value = Scalar[dtype]( - -170141183460469231731687303715884105728 - ) - max_value = Scalar[dtype]( - 170141183460469231731687303715884105727 - ) - elif dtype == DType.int64: - min_value = Scalar[dtype].MIN - max_value = Scalar[dtype].MAX - elif dtype == DType.int32: - min_value = Scalar[dtype].MIN - max_value = Scalar[dtype].MAX - elif dtype == DType.int16: - min_value = Scalar[dtype].MIN - max_value = Scalar[dtype].MAX - elif dtype == DType.int8: - min_value = Scalar[dtype].MIN - max_value = Scalar[dtype].MAX - else: - raise Error( - "Error in `from_scalar()`: Unsupported integral type" - ) - - if value == min_value: - remainder = max_value - is_min = True - else: - remainder = -value - - while remainder != 0: - quotient = remainder // 1_000_000_000 - remainder = remainder % 1_000_000_000 - list_of_words.append(UInt32(remainder)) - remainder = quotient - if is_min: - list_of_words[0] += 1 + constrained[ + dtype.is_floating_point(), "dtype must be floating-point." + ]() - return Self(coefficient=BigUInt(list_of_words^), scale=0, sign=sign) - - else: # floating-point - if value != value: # Check for NaN - raise Error( - "Error in `from_scalar()`: Cannot convert NaN to BigUInt" - ) - # Convert to string with full precision - try: - return Self.from_string(String(value)) - except e: - raise Error("Error in `from_scalar()`: ", e) + if value == 0: + return Self(coefficient=BigUInt.ZERO, scale=0, sign=False) - return Self( - coefficient=BigUInt(UInt32(0)), scale=0, sign=sign - ) # Default case + if value != value: # Check for NaN + raise Error("`from_scalar()`: Cannot convert NaN to BigUInt") + # Convert to string with full precision + try: + return Self.from_string(String(value)) + except e: + raise Error( + "`from_scalar()`: Cannot get decimal from string\nTrace back: " + + String(e), + ) @staticmethod fn from_string(value: String) raises -> Self: @@ -493,6 +485,38 @@ struct BigDecimal( self, exponent, precision=28 ) + # ===------------------------------------------------------------------=== # + # Basic binary right-side arithmetic operation dunders + # These methods are called to implement the binary arithmetic operations + # (+, -, *, @, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |) + # ===------------------------------------------------------------------=== # + + @always_inline + fn __radd__(self, other: Self) raises -> Self: + return decimojo.bigdecimal.arithmetics.add(self, other) + + @always_inline + fn __rsub__(self, other: Self) raises -> Self: + return decimojo.bigdecimal.arithmetics.subtract(other, self) + + @always_inline + fn __rmul__(self, other: Self) raises -> Self: + return decimojo.bigdecimal.arithmetics.multiply(self, other) + + @always_inline + fn __rfloordiv__(self, other: Self) raises -> Self: + return decimojo.bigdecimal.arithmetics.truncate_divide(other, self) + + @always_inline + fn __rmod__(self, other: Self) raises -> Self: + return decimojo.bigdecimal.arithmetics.truncate_modulo( + other, self, precision=28 + ) + + @always_inline + fn __rpow__(self, base: Self) raises -> Self: + return decimojo.bigdecimal.exponential.power(base, self, precision=28) + # ===------------------------------------------------------------------=== # # Basic binary augmented arithmetic assignments dunders # These methods are called to implement the binary augmented arithmetic @@ -586,6 +610,10 @@ struct BigDecimal( except e: return self + # ===------------------------------------------------------------------=== # + # Other dunders + # ===------------------------------------------------------------------=== # + # ===------------------------------------------------------------------=== # # Mathematical methods that do not implement a trait (not a dunder) # ===------------------------------------------------------------------=== # diff --git a/src/decimojo/bigdecimal/exponential.mojo b/src/decimojo/bigdecimal/exponential.mojo index 3f1f0ed..3f9a120 100644 --- a/src/decimojo/bigdecimal/exponential.mojo +++ b/src/decimojo/bigdecimal/exponential.mojo @@ -512,7 +512,7 @@ fn sqrt(x: BigDecimal, precision: Int) raises -> BigDecimal: if odd_ndigits_frac_part: value = value * UInt128(10) var sqrt_value = decimojo.utility.sqrt(value) - var sqrt_value_biguint = BigUInt.from_scalar(sqrt_value) + var sqrt_value_biguint = BigUInt.from_unsigned_integral_scalar(sqrt_value) guess = BigDecimal( sqrt_value_biguint, sqrt_value_biguint.number_of_digits() - ndigits_int_part_sqrt, diff --git a/src/decimojo/bigint/bigint.mojo b/src/decimojo/bigint/bigint.mojo index 49ba910..2d5f7b8 100644 --- a/src/decimojo/bigint/bigint.mojo +++ b/src/decimojo/bigint/bigint.mojo @@ -27,9 +27,10 @@ from memory import UnsafePointer import testing import time -from decimojo.biguint.biguint import BigUInt import decimojo.bigint.arithmetics import decimojo.bigint.comparison +from decimojo.bigdecimal.bigdecimal import BigDecimal +from decimojo.biguint.biguint import BigUInt import decimojo.str # Type aliases @@ -69,6 +70,12 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable): self.magnitude = BigUInt() self.sign = False + @implicit + fn __init__(out self, magnitude: BigUInt): + """Constructs a BigInt from a BigUInt object.""" + self.magnitude = magnitude + self.sign = False + fn __init__(out self, magnitude: BigUInt, sign: Bool): """Initializes a BigInt from a BigUInt and a sign. @@ -81,7 +88,7 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable): self.sign = sign fn __init__(out self, words: List[UInt32], sign: Bool): - """Initializes a BigInt from a List of UInt32 and a sign. + """***UNSAFE!*** Initializes a BigInt from a List of UInt32 and a sign. It does not check whether the list is empty or the words are invalid. See `from_list()` for safer initialization. @@ -100,7 +107,8 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable): self.sign = sign fn __init__(out self, owned *words: UInt32, sign: Bool) raises: - """Initializes a BigInt from raw components. + """***UNSAFE!*** Initializes a BigInt from raw components. + It does not check whether the words are invalid. See `from_words()` for safer initialization. Args: @@ -125,12 +133,6 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable): self.magnitude = BigUInt(List[UInt32](elements=words^)) self.sign = sign - fn __init__(out self, value: Int) raises: - """Initializes a BigInt from an Int. - See `from_int()` for more information. - """ - self = Self.from_int(value) - fn __init__(out self, value: String) raises: """Initializes a BigInt from a string representation. See `from_string()` for more information. @@ -140,6 +142,23 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable): except e: raise Error("Error in `BigInt.__init__()` with String: ", e) + @implicit + fn __init__(out self, value: Int): + """Initializes a BigInt from an `Int` object. + See `from_int()` for more information. + """ + self = Self.from_int(value) + + @implicit + fn __init__(out self, value: Scalar): + """Constructs a BigInt from an integral scalar. + This includes all SIMD integral types, such as Int8, Int16, UInt32, etc. + + Constraints: + The dtype of the scalar must be integral. + """ + self = Self.from_integral_scalar(value) + # ===------------------------------------------------------------------=== # # Constructing methods that are not dunders # @@ -233,21 +252,29 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable): return Self(BigUInt(words^), sign) @staticmethod - fn from_uint128(value: UInt128, sign: Bool = False) raises -> Self: - """Initializes a BigInt from a UInt128 value. + fn from_integral_scalar[dtype: DType, //](value: SIMD[dtype, 1]) -> Self: + """Initializes a BigInt from an integral scalar. + This includes all SIMD integral types, such as Int8, Int16, UInt32, etc. + + Constraints: + The dtype must be integral. Args: - value: The UInt128 value to be converted to BigInt. - sign: The sign of the BigInt. Default is False. + value: The Scalar value to be converted to BigInt. Returns: - The BigInt representation of the UInt128 value. + The BigInt representation of the Scalar value. """ + + constrained[dtype.is_integral(), "dtype must be integral."]() + if value == 0: return Self() - var magnitude = BigUInt.from_scalar(value) - return Self(magnitude^, sign) + return Self( + magnitude=BigUInt.from_absolute_integral_scalar(value), + sign=True if value < 0 else False, + ) @staticmethod fn from_string(value: String) raises -> Self: @@ -476,32 +503,28 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable): # ===------------------------------------------------------------------=== # @always_inline - fn __radd__(self, other: Int) raises -> Self: - return decimojo.bigint.arithmetics.add(self, Self.from_int(other)) + fn __radd__(self, other: Self) raises -> Self: + return decimojo.bigint.arithmetics.add(self, other) @always_inline - fn __rsub__(self, other: Int) raises -> Self: - return decimojo.bigint.arithmetics.subtract(Self.from_int(other), self) + fn __rsub__(self, other: Self) raises -> Self: + return decimojo.bigint.arithmetics.subtract(other, self) @always_inline - fn __rmul__(self, other: Int) raises -> Self: - return decimojo.bigint.arithmetics.multiply(self, Self.from_int(other)) + fn __rmul__(self, other: Self) raises -> Self: + return decimojo.bigint.arithmetics.multiply(self, other) @always_inline - fn __rfloordiv__(self, other: Int) raises -> Self: - return decimojo.bigint.arithmetics.floor_divide( - Self.from_int(other), self - ) + fn __rfloordiv__(self, other: Self) raises -> Self: + return decimojo.bigint.arithmetics.floor_divide(other, self) @always_inline - fn __rmod__(self, other: Int) raises -> Self: - return decimojo.bigint.arithmetics.floor_modulo( - Self.from_int(other), self - ) + fn __rmod__(self, other: Self) raises -> Self: + return decimojo.bigint.arithmetics.floor_modulo(other, self) @always_inline - fn __rpow__(self, base: Int) raises -> Self: - return Self(base).power(self) + fn __rpow__(self, base: Self) raises -> Self: + return base.power(self) # ===------------------------------------------------------------------=== # # Basic binary augmented arithmetic assignments dunders @@ -621,6 +644,14 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable): """Returns True if self != other.""" return decimojo.bigint.comparison.not_equal(self, Self.from_int(other)) + # ===------------------------------------------------------------------=== # + # Other dunders + # ===------------------------------------------------------------------=== # + + fn __merge_with__[other_type: __type_of(BigDecimal)](self) -> BigDecimal: + "Merges this BigInt with a BigDecimal into a BigDecimal." + return BigDecimal(self) + # ===------------------------------------------------------------------=== # # Mathematical methods that do not implement a trait (not a dunder) # ===------------------------------------------------------------------=== # diff --git a/src/decimojo/biguint/biguint.mojo b/src/decimojo/biguint/biguint.mojo index 054000d..53dcb95 100644 --- a/src/decimojo/biguint/biguint.mojo +++ b/src/decimojo/biguint/biguint.mojo @@ -68,7 +68,7 @@ struct BigUInt(Absable, IntableRaising, Stringable, Writable): @staticmethod fn zero() -> Self: """Returns a BigUInt with value 0.""" - return Self(words=List[UInt32](UInt32(0))) + return Self() @always_inline @staticmethod @@ -137,11 +137,12 @@ struct BigUInt(Absable, IntableRaising, Stringable, Writable): """ self = Self.from_int(value) - fn __init__(out self, value: Scalar) raises: - """Initializes a BigUInt from a Mojo Scalar. - See `from_scalar()` for more information. + @implicit + fn __init__(out self, value: Scalar): + """Initializes a BigUInt from an unsigned integral scalar. + See `from_unsigned_integral_scalar()` for more information. """ - self = Self.from_scalar(value) + self = Self.from_unsigned_integral_scalar(value) fn __init__(out self, value: String, ignore_sign: Bool = False) raises: """Initializes a BigUInt from a string representation. @@ -243,51 +244,116 @@ struct BigUInt(Absable, IntableRaising, Stringable, Writable): return Self(list_of_words^) @staticmethod - fn from_scalar[dtype: DType, //](value: Scalar[dtype]) raises -> Self: - """Initializes a BigUInt from a Mojo Scalar. + fn from_unsigned_integral_scalar[ + dtype: DType, // + ](value: SIMD[dtype, 1]) -> Self: + """Initializes a BigUInt from an unsigned integral scalar. + This includes all SIMD unsigned integral types, such as UInt8, UInt16, + UInt32, UInt64, etc. + + Constraints: + The dtype must be integral and unsigned. Args: value: The Scalar value to be converted to BigUInt. Returns: The BigUInt representation of the Scalar value. - - Notes: - If the value is a floating-point number, it is converted to a string - with full precision before converting to BigUInt. - If the fractional part is not zero, an error is raised. """ - if value < 0: - raise Error("Error in `from_scalar()`: The value is negative") + + constrained[ + dtype.is_integral() and dtype.is_unsigned(), + "dtype must be unsigned integral.", + ]() + + @parameter + if ( + (dtype == DType.uint8) + or (dtype == DType.uint16) + or (dtype == DType.uint32) + ): + return Self(words=List[UInt32](UInt32(value))) if value == 0: return Self() + var list_of_words = List[UInt32]() + var remainder: Scalar[dtype] = value + var quotient: Scalar[dtype] + + while remainder != 0: + quotient = remainder // 1_000_000_000 + remainder = remainder % 1_000_000_000 + list_of_words.append(UInt32(remainder)) + remainder = quotient + + return Self(words=list_of_words^) + + @staticmethod + fn from_absolute_integral_scalar[ + dtype: DType, // + ](value: SIMD[dtype, 1]) -> Self: + """Initializes a BigUInt from an integral scalar and ignores the sign. + This includes all SIMD integral types, such as UInt8, UInt16, Int32, + Int64, Int128, etc. + + Constraints: + The dtype must be integral and unsigned. + + Args: + value: The Scalar value to be converted to BigUInt. + + Returns: + The BigUInt representation of the Scalar value. + """ + + constrained[dtype.is_integral(), "dtype must be integral."]() + @parameter - if dtype.is_integral(): + if ( + (dtype == DType.uint8) + or (dtype == DType.uint16) + or (dtype == DType.uint32) + ): + # For types that are smaller than word size + # We can directly convert them to UInt32 + return Self(words=List[UInt32](UInt32(value))) + + elif (dtype == DType.int8) or (dtype == DType.int16): + # For signed types that are smaller than 1_000_000_000, + # we need to handle it differently + if value < 0: + # Because -Int16.MIN == Int16.MAX + 1, + # we need to handle the case by converting it to Int32 + # before taking the absolute value. + return Self(List[UInt32](UInt32(-Int32(value)))) + else: + return Self(List[UInt32](UInt32(value))) + + else: + if value == 0: + return BigUInt.ZERO + + var sign = True if value < 0 else False + var list_of_words = List[UInt32]() var remainder: Scalar[dtype] = value var quotient: Scalar[dtype] - while remainder != 0: - quotient = remainder // 1_000_000_000 - remainder = remainder % 1_000_000_000 - list_of_words.append(UInt32(remainder)) - remainder = quotient - return Self(list_of_words^) - else: - if value != value: # Check for NaN - raise Error( - "Error in `BigUInt.from_scalar()`: Cannot convert NaN to" - " BigUInt" - ) - # Convert to string with full precision - try: - return Self.from_string(String(value)) - except e: - raise Error("Error in `BigUInt.from_scalar()`: ", e) + if sign: + while remainder != 0: + quotient = remainder // (-1_000_000_000) + remainder = remainder % (-1_000_000_000) + list_of_words.append(UInt32(-remainder)) + remainder = -quotient + else: + while remainder != 0: + quotient = remainder // 1_000_000_000 + remainder = remainder % 1_000_000_000 + list_of_words.append(UInt32(remainder)) + remainder = quotient - return Self() + return Self(list_of_words^) @staticmethod fn from_string(value: String, ignore_sign: Bool = False) raises -> BigUInt: @@ -669,6 +735,18 @@ struct BigUInt(Absable, IntableRaising, Stringable, Writable): """Returns True if self != other.""" return decimojo.biguint.comparison.not_equal(self, other) + # ===------------------------------------------------------------------=== # + # Other dunders + # ===------------------------------------------------------------------=== # + + fn __merge_with__[other_type: __type_of(BigInt)](self) -> BigInt: + "Merges this BigUInt with a BigInt into a BigInt." + return BigInt(self) + + fn __merge_with__[other_type: __type_of(BigDecimal)](self) -> BigDecimal: + "Merges this BigUInt with a BigDecimal into a BigDecimal." + return BigDecimal(self) + # ===------------------------------------------------------------------=== # # Mathematical methods that do not implement a trait (not a dunder) # ===------------------------------------------------------------------=== #