Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 59 additions & 114 deletions src/decimojo/decimal.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -229,20 +229,13 @@ struct Decimal(
).format(scale)
)

self.low = low
self.mid = mid
self.high = high

# First set the flags without capping to initialize properly
var flags: UInt32 = 0

# Set the initial scale (may be higher than MAX_SCALE)
flags |= (scale << Self.SCALE_SHIFT) & Self.SCALE_MASK
flags |= sign << 31

# Set the sign bit if negative
if sign:
flags |= Self.SIGN_MASK

self.low = low
self.mid = mid
self.high = high
self.flags = flags

fn __init__(out self, integer: Int):
Expand All @@ -253,114 +246,22 @@ struct Decimal(
Since Int is a 64-bit type in Mojo, this constructor can only
handle values up to 64 bits. The `high` field will always be 0.
"""
self.flags = 0

if integer == 0:
self.low = 0
self.mid = 0
self.high = 0
return

if integer < 0:
# Set sign bit for negative integers
self.flags = Self.SIGN_MASK

# Handle negative value by taking absolute value first
abs_value = UInt64(-integer)

# Set the coefficient fields
# `high` will always be 0 because Int is 64-bit
self.low = UInt32(abs_value & 0xFFFFFFFF)
self.mid = UInt32((abs_value >> 32) & 0xFFFFFFFF)
self.high = 0
else:
# Positive integer
self.low = UInt32(integer & 0xFFFFFFFF)
self.mid = UInt32((integer >> 32) & 0xFFFFFFFF)
self.high = 0

fn __init__(out self, integer: Int128):
"""
Initializes a Decimal from an Int128 value.
***WARNING***: This constructor can only handle values up to 96 bits.
"""
# Initialize flags
self.flags = 0

if integer == 0:
self.low = 0
self.mid = 0
self.high = 0
return

if integer < 0:
# Set sign bit for negative integers
# Set sign bit if negative
var is_negative = integer < 0
if is_negative:
self.flags = Self.SIGN_MASK

# Handle negative value by taking absolute value first
var abs_value = -integer
# Take absolute value
var abs_value = UInt64(integer if integer >= 0 else -integer)

# Set the coefficient fields
# `high` will always be 0 because Int is 64-bit
self.low = UInt32(abs_value & 0xFFFFFFFF)
self.mid = UInt32((abs_value >> 32) & 0xFFFFFFFF)
self.high = UInt32((abs_value >> 64) & 0xFFFFFFFF)
else:
# Positive integer
print(integer)
self.low = UInt32(integer & 0xFFFFFFFF)
self.mid = UInt32((integer >> 32) & 0xFFFFFFFF)
self.high = UInt32((integer >> 64) & 0xFFFFFFFF)

# TODO: Add arguments to specify the scale and sign
fn __init__(out self, integer: UInt64):
"""
Initializes a Decimal from an UInt64 value.
The `high` word will always be 0.
"""
self.low = UInt32(integer & 0xFFFFFFFF)
self.mid = UInt32((integer >> 32) & 0xFFFFFFFF)
# Set the coefficient fields (same for both positive and negative)
self.low = UInt32(abs_value & 0xFFFFFFFF)
self.mid = UInt32((abs_value >> 32) & 0xFFFFFFFF)
self.high = 0
self.flags = 0

# TODO: Create method `from_uint128` to handle UInt128 values
fn __init__(
out self, integer: UInt128, scale: UInt32 = 0, sign: Bool = False
) raises:
"""
Initializes a Decimal from an UInt128 value.
***WARNING***: This constructor can only handle values up to 96 bits.
"""
var low = UInt32(integer & 0xFFFFFFFF)
var mid = UInt32((integer >> 32) & 0xFFFFFFFF)
var high = UInt32((integer >> 64) & 0xFFFFFFFF)

try:
self = Decimal(low, mid, high, scale, sign)
except e:
raise Error(
"Error in Decimal constructor with UInt128, scale, and sign: ",
e,
)

fn __init__(
out self, integer: UInt256, scale: UInt32 = 0, sign: Bool = False
) raises:
"""
Initializes a Decimal from an UInt256 value.
***WARNING***: This constructor can only handle values up to 96 bits.
"""
var low = UInt32(integer & 0xFFFFFFFF)
var mid = UInt32((integer >> 32) & 0xFFFFFFFF)
var high = UInt32((integer >> 64) & 0xFFFFFFFF)

try:
self = Decimal(low, mid, high, scale, sign)
except e:
raise Error(
"Error in Decimal constructor with UInt256, scale, and sign: ",
e,
)
self = Decimal(low, mid, high, scale, sign)

fn __init__(out self, value: String) raises:
"""
Expand Down Expand Up @@ -413,6 +314,50 @@ struct Decimal(

return result

@staticmethod
fn from_uint128(
value: UInt128, scale: UInt32 = 0, sign: Bool = False
) raises -> Decimal:
"""
Initializes a Decimal from a UInt128 value.

Args:
value: The UInt128 value to convert to Decimal.
scale: The number of decimal places (0-28).
sign: True if the number is negative.

Returns:
The Decimal representation of the UInt128 value.

Raises:
Error: If the most significant word of the UInt128 is not zero.
Error: If the scale is greater than MAX_SCALE.
"""

if value >> 96 != 0:
raise Error(
String(
"Error in Decimal constructor with UInt128: Value must"
" fit in 96 bits, but got {}"
).format(value)
)

if scale > Self.MAX_SCALE:
raise Error(
String(
"Error in Decimal constructor with five components: Scale"
" must be between 0 and 28, but got {}"
).format(scale)
)

var result = UnsafePointer[UInt128].address_of(value).bitcast[
Decimal
]()[]
result.flags |= (scale << Self.SCALE_SHIFT) & Self.SCALE_MASK
result.flags |= sign << 31

return result

@staticmethod
fn from_string(value: String) raises -> Decimal:
"""
Expand Down Expand Up @@ -637,7 +582,7 @@ struct Decimal(
# )
scale = Decimal.MAX_SCALE

return Decimal(coef, scale, mantissa_sign)
return Decimal.from_uint128(coef, scale, mantissa_sign)

else:
var ndigits_coef = decimojo.utility.number_of_digits(coef)
Expand Down Expand Up @@ -667,7 +612,7 @@ struct Decimal(
)
scale_of_truncated_coef = Decimal.MAX_SCALE

return Decimal(
return Decimal.from_uint128(
truncated_coef, scale_of_truncated_coef, mantissa_sign
)

Expand Down
8 changes: 5 additions & 3 deletions src/decimojo/maths/exp.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -141,19 +141,21 @@ fn sqrt(x: Decimal) raises -> Decimal:
# For numbers with zero scale (true integers)
if x_scale == 0:
var float_sqrt = builtin_math.sqrt(Float64(x_coef))
guess = Decimal(UInt128(float_sqrt))
guess = Decimal.from_uint128(UInt128(float_sqrt))
# print("DEBUG: scale = 0")

# For numbers with even scale
elif x_scale % 2 == 0:
var float_sqrt = builtin_math.sqrt(Float64(x_coef))
guess = Decimal(UInt128(float_sqrt), scale=x_scale >> 1, sign=False)
guess = Decimal.from_uint128(
UInt128(float_sqrt), scale=x_scale >> 1, sign=False
)
# print("DEBUG: scale is even")

# For numbers with odd scale
else:
var float_sqrt = builtin_math.sqrt(Float64(x_coef)) * Float64(3.15625)
guess = Decimal(
guess = Decimal.from_uint128(
UInt128(float_sqrt), scale=(x_scale + 1) >> 1, sign=False
)
# print("DEBUG: scale is odd")
Expand Down
10 changes: 7 additions & 3 deletions src/decimojo/maths/rounding.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ fn round(

# In other cases, return the result
else:
return Decimal(
return Decimal.from_uint128(
res_coef, scale=ndigits, sign=number.is_negative()
)

Expand All @@ -134,12 +134,16 @@ fn round(
)

if ndigits >= 0:
return Decimal(res_coef, scale=ndigits, sign=number.is_negative())
return Decimal.from_uint128(
res_coef, scale=ndigits, sign=number.is_negative()
)

# if `ndigits` is negative and `ndigits_to_keep` >= 0, scale up the result
elif ndigits_to_keep >= 0:
res_coef *= UInt128(10) ** (-ndigits)
return Decimal(res_coef, scale=0, sign=number.is_negative())
return Decimal.from_uint128(
res_coef, scale=0, sign=number.is_negative()
)

# if `ndigits` is negative and `ndigits_to_keep` < 0, return 0
else:
Expand Down