Skip to content

Commit

Permalink
Various Decimal usability tweaks (#10517)
Browse files Browse the repository at this point in the history
  • Loading branch information
GregoryTravis authored Jul 16, 2024
1 parent cd1d617 commit 0268cbb
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 52 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@
- [Rename `Map` to `Dictionary` and `Set` to `Hashset`.][10474]
- [Compare two objects with `Ordering.compare` and define comparator with
`Comparable.new`][10468]
- [Added `dec` construction function for creating `Decimal`s.][10517]

[10434]: https://github.com/enso-org/enso/pull/10434
[10445]: https://github.com/enso-org/enso/pull/10445
[10466]: https://github.com/enso-org/enso/pull/10466
[10467]: https://github.com/enso-org/enso/pull/10467
[10474]: https://github.com/enso-org/enso/pull/10474
[10517]: https://github.com/enso-org/enso/pull/10517

# Enso 2024.2

Expand Down
133 changes: 93 additions & 40 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Decimal.enso
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import project.Data.Numeric.Internal.Decimal_Internal
import project.Data.Numeric.Math_Context.Math_Context
import project.Data.Numeric.Rounding_Mode.Rounding_Mode
import project.Data.Text.Text
import project.Data.Vector.Vector
import project.Error.Error
import project.Errors.Illegal_Argument.Illegal_Argument
import project.Nothing.Nothing
Expand Down Expand Up @@ -89,7 +90,7 @@ type Decimal
Value (big_decimal : BigDecimal)

## ICON input_number
Construct a `Decimal` from a string or integer.
Construct a `Decimal` from a `Text`, `Integer` or `Float`.

Arguments:
- x: The `Text`, `Integer`, or `Float` to construct a `Decimal` from.
Expand All @@ -111,14 +112,24 @@ type Decimal
if a `Math_Context` value is explicitly passed.

^ Example
Create a `Decimal` from a string.
Create a `Decimal` from a `Text`.

c = Decimal.new "12.345"

^ Example
Create a `Decimal` from an `Integer`.

c = Decimal.new 12345

^ Example
Create a `Decimal` from a `Float`.

c = Decimal.new 12.345
new : Text | Integer | Float -> Math_Context | Nothing -> Decimal ! Arithmetic_Error | Number_Parse_Error
new (x : Text | Integer | Float) (mc : Math_Context | Nothing = Nothing) -> Decimal ! Arithmetic_Error | Number_Parse_Error =
handle_java_exception <|
case x of
_ : Text -> Decimal.from_string x mc
_ : Text -> Decimal.from_text x mc
_ : Integer -> Decimal.from_integer x mc
_ : Float -> Decimal.from_float x mc

Expand All @@ -145,11 +156,11 @@ type Decimal
if a `Math_Context` value is explicitly passed.

^ Example
Create a `Decimal` from a string.
Create a `Decimal` from a `Text`.

d = Decimal.from_string "12.345"
from_string : Text -> Math_Context | Nothing -> Decimal ! Number_Parse_Error
from_string (s : Text) (mc : Math_Context | Nothing = Nothing) -> Decimal ! Number_Parse_Error =
d = Decimal.from_text "12.345"
from_text : Text -> Math_Context | Nothing -> Decimal ! Number_Parse_Error
from_text (s : Text) (mc : Math_Context | Nothing = Nothing) -> Decimal ! Number_Parse_Error =
handle_java_exception <| handle_number_format_exception <|
case mc of
_ : Math_Context -> Decimal.Value <| handle_precision_loss s <| Decimal_Utils.fromString s mc.math_context
Expand All @@ -172,7 +183,7 @@ type Decimal
if a `Math_Context` value is explicitly passed.

^ Example
Create a `Decimal` from an integer.
Create a `Decimal` from an `Integer`.

d = Decimal.from_integer 12
from_integer : Integer -> Math_Context | Nothing -> Decimal
Expand Down Expand Up @@ -207,7 +218,7 @@ type Decimal
- If `f` is NaN or +/-Inf, an Illegal_Argument error is thrown.

^ Example
Create a `Decimal` from a float.
Create a `Decimal` from a `Float`.

d = Decimal.from_integer 12.345
from_float : Float -> Math_Context | Nothing -> Decimal ! Arithmetic_Error | Illegal_Argument
Expand Down Expand Up @@ -343,7 +354,7 @@ type Decimal
c = a + b
# => Decimal.new 30.55
+ : Decimal -> Decimal
+ self (that : Decimal) = self.add that
+ self (that : Decimal) -> Decimal ! Arithmetic_Error = self.add that

## ALIAS minus
GROUP Operators
Expand Down Expand Up @@ -402,7 +413,7 @@ type Decimal
c = a - b
# => Decimal.new 10.11
- : Decimal -> Decimal
- self (that : Decimal) = self.subtract that
- self (that : Decimal) -> Decimal ! Arithmetic_Error = self.subtract that

## ALIAS times
GROUP Operators
Expand Down Expand Up @@ -460,7 +471,7 @@ type Decimal
c = a * b
# => Decimal.new 207.7726
* : Decimal -> Decimal
* self (that : Decimal) = self.multiply that
* self (that : Decimal) -> Decimal ! Arithmetic_Error = self.multiply that

## GROUP Operators
ICON math
Expand Down Expand Up @@ -524,7 +535,7 @@ type Decimal
c = a / b
# => Decimal.new 45.67
/ : Decimal -> Decimal
/ self (that : Decimal) = self.divide that
/ self (that : Decimal) -> Decimal ! Arithmetic_Error = self.divide that

## ALIAS modulo, modulus
GROUP Operators
Expand Down Expand Up @@ -554,7 +565,7 @@ type Decimal
remainder = Decimal.new -5 . remainder 3
# => -2
remainder : Decimal -> Decimal
remainder self (that : Decimal) =
remainder self (that : Decimal) -> Decimal =
handle_java_exception <|
Decimal.Value (self.big_decimal.remainder that.big_decimal)

Expand Down Expand Up @@ -586,7 +597,7 @@ type Decimal
remainder = Decimal.new -5 % 3
# => -2
% : Decimal -> Decimal
% self (that : Decimal) = self.remainder that
% self (that : Decimal) -> Decimal = self.remainder that

## GROUP Math
ICON math
Expand Down Expand Up @@ -640,7 +651,7 @@ type Decimal
Decimal.new "2.25" . pow (Decimal.new "5")
# => 57.6650390625
pow : Integer -> Decimal
pow self exp:Integer =
pow self exp:Integer -> Decimal =
## If `exp` is an integer that does not fit in a Java Integer,
UnsuppUnsupported_Argument_Types is raised, so we convert that to an
Arithmetic_Error.
Expand All @@ -667,7 +678,7 @@ type Decimal
Decimal.new "2.25" ^ Decimal.new "5"
# => 57.6650390625
^ : Integer -> Decimal
^ self exp:Integer = self.pow exp
^ self exp:Integer -> Decimal = self.pow exp

## GROUP Operators
ICON operators
Expand All @@ -679,7 +690,7 @@ type Decimal
5.1.negate
# => Decimal.new -5.1
negate : Decimal
negate self = Decimal.Value self.big_decimal.negate
negate self -> Decimal = Decimal.Value self.big_decimal.negate

## GROUP Math
ICON math
Expand Down Expand Up @@ -755,9 +766,11 @@ type Decimal
# => 2345
to_integer : Integer
to_integer self =
i = self.big_decimal.toBigInteger
if self == i then i else
Warning.attach (Loss_Of_Numeric_Precision.Warning self i) i
as_biginteger = self.big_decimal.toBigInteger
back_to_decimal = BigDecimal.new as_biginteger
are_equal = (self.big_decimal.compareTo back_to_decimal) == 0
if are_equal then as_biginteger else
Warning.attach (Loss_Of_Numeric_Precision.Warning self as_biginteger) as_biginteger

## GROUP Conversions
ICON convert
Expand Down Expand Up @@ -791,8 +804,8 @@ type Decimal
d = Decimal.new "23.45"
d.to_float
# => 23.45
to_float : Integer
to_float self =
to_float : Float
to_float self -> Float =
f = self.big_decimal.doubleValue
if f.is_finite then attach_loss_of_numeric_precision self f else
message = "Outside representable Float range (approximately (-1.8E308, 1.8E308))"
Expand Down Expand Up @@ -855,7 +868,7 @@ type Decimal

## GROUP Rounding
ICON math
Computes the nearest integer equal to or above this number.
Computes the nearest `Integer` equal to or above this number.

> Example
Compute the ceiling of 12.34.
Expand All @@ -873,7 +886,7 @@ type Decimal

## GROUP Rounding
ICON math
Computes the nearest integer equal to or below this number.
Computes the nearest `Integer` equal to or below this number.

> Example
Compute the floor of 12.34.
Expand All @@ -893,8 +906,8 @@ type Decimal
GROUP Rounding
ICON math

Truncate a number to an integer to by dropping the fractional part. This
is equivalent to "round-toward-zero".
Truncate a number to an `Integer` to by dropping the fractional part.
This is equivalent to "round-toward-zero".

> Example
Compute the truncation of 12.34
Expand Down Expand Up @@ -942,7 +955,7 @@ type Decimal
@format make_number_format_selector
@locale Locale.default_widget
format : Text -> Locale -> Text
format self format:Text="" locale:Locale=Locale.default =
format self format:Text="" locale:Locale=Locale.default -> Text =
symbols = DecimalFormatSymbols.new locale.java_locale
formatter = DecimalFormat.new format symbols
formatter.format self.big_decimal
Expand Down Expand Up @@ -986,50 +999,90 @@ type Decimal
Decimal.parse "123.456.789,87654" locale=Locale.italy
# => 123456789.87654
parse : Text -> Locale | Nothing -> Decimal ! Number_Parse_Error
parse text locale:(Locale | Nothing)=Nothing = case locale of
Nothing -> Decimal.from_string text
parse text locale:(Locale | Nothing)=Nothing -> Decimal ! Number_Parse_Error = case locale of
Nothing -> Decimal.from_text text
Locale.Value java_locale -> Panic.catch ParseException ((NumberFormat.getInstance java_locale).parse text) _->
Error.throw (Number_Parse_Error.Error text)

## PRIVATE
precision : Integer
precision self = self.big_decimal.precision
precision self -> Integer = self.big_decimal.precision

## PRIVATE
scale : Integer
scale self = self.big_decimal.scale
scale self -> Integer = self.big_decimal.scale

## PRIVATE
with_scale : Integer -> Decimal
private with_scale self new_scale:Integer =
private with_scale self new_scale:Integer -> Decimal =
if self.scale == new_scale then self else
Decimal.Value (self.big_decimal.setScale new_scale)

## PRIVATE
unscaled_value : Integer
unscaled_value self = self.big_decimal.unscaledValue
unscaled_value self -> Integer = self.big_decimal.unscaledValue

## PRIVATE
internal_representation : [Integer]
internal_representation self = [self.unscaled_value, self.precision, self.scale]
internal_representation : Vector Integer
internal_representation self -> Vector Integer = [self.unscaled_value, self.precision, self.scale]

## PRIVATE
Note: the underlying Java `BigDecimal` implementation is not affected by
locale.
to_text : Text
to_text self = self.big_decimal.toString
to_text self -> Text = self.big_decimal.toString

## PRIVATE
Note: the underlying Java `BigDecimal` implementation is not affected by
locale.
to_display_text : Text
to_display_text self = self.big_decimal.toString
to_display_text self -> Text = self.big_decimal.toString

## PRIVATE
Note: the underlying Java `BigDecimal` implementation is not affected by
locale.
to_text_without_scientific_notation : Text
to_text_without_scientific_notation self = self.big_decimal.toPlainString
to_text_without_scientific_notation self -> Text = self.big_decimal.toPlainString

## ICON input_number
Construct a `Decimal` from a `Text`, `Integer` or `Float`.

Arguments:
- x: The `Text`, `Integer`, or `Float` to construct a `Decimal` from.
- mc: The `Math_Context` to use to specify precision and `Rounding_Mode`.
If a `Math_Context` is used, there is a possibility of a loss of
precision.

? Number Format

The textual format for a Decimal is defined at
https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html#BigDecimal-java.lang.String-.

! Error Conditions

- If the `Text` argument is incorrectly formatted, a `Number_Parse_Error`
is thrown.
- If the construction of the Decimal results in a loss of precision, a
`Loss_Of_Numeric_Precision` warning is attached. This can only happen
if a `Math_Context` value is explicitly passed.

^ Example
Create a `Decimal` from a `Text`.

c = dec "12.345"

^ Example
Create a `Decimal` from an `Integer`.

c = dec 12345

^ Example
Create a `Decimal` from a `Float`.

c = dec 12.345
dec : Text | Integer | Float -> Math_Context | Nothing -> Decimal ! Arithmetic_Error | Number_Parse_Error
dec (x : Text | Integer | Float) (mc : Math_Context | Nothing = Nothing) -> Decimal ! Arithmetic_Error | Number_Parse_Error =
Decimal.new x mc

## PRIVATE
handle_number_format_exception ~action =
Expand Down Expand Up @@ -1065,7 +1118,7 @@ Comparable.from (that : Decimal) = Comparable.new that Decimal_Comparator
Comparable.from (that : Number) = Comparable.new that Decimal_Comparator

## PRIVATE
Decimal.from (that : Text) = Decimal.from_string that
Decimal.from (that : Text) = Decimal.from_text that

## PRIVATE
Decimal.from (that : Integer) = Decimal.new that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ Decimal.from (that:JS_Object) =
case that.get "type" == "Decimal" && ["value", "scale", "precision"].all that.contains_key of
True ->
math_context = Math_Context.new (that.at "precision")
raw_value = Decimal.from_string (that.at "value") math_context
raw_value = Decimal.from_text (that.at "value") math_context
raw_value.with_scale (that.at "scale")
False -> Error.throw (Illegal_Argument.Error "Invalid JS_Object for Decimal.")

Expand Down
1 change: 1 addition & 0 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export project.Any.Any
export project.Data
export project.Data.Array.Array
export project.Data.Decimal.Decimal
export project.Data.Decimal.dec
export project.Data.Dictionary.Dictionary
export project.Data.Filter_Condition.Filter_Action
export project.Data.Filter_Condition.Filter_Condition
Expand Down
Loading

0 comments on commit 0268cbb

Please sign in to comment.