mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 11:52:59 +03:00
Mixed Decimal/Float operations throw error or attach warning (#10725)
This commit is contained in:
parent
4d2cc04e17
commit
e836373d9b
@ -14,11 +14,14 @@
|
||||
Cloud.][10660]
|
||||
- [Added Newline option to Text_Cleanse/Text_Replace.][10761]
|
||||
- [Support for reading from Tableau Hyper files.][10733]
|
||||
- [Mixed Decimal/Float arithmetic now throws an error; mixed comparisons now
|
||||
attach warnings.][10725]
|
||||
|
||||
[10614]: https://github.com/enso-org/enso/pull/10614
|
||||
[10660]: https://github.com/enso-org/enso/pull/10660
|
||||
[10761]: https://github.com/enso-org/enso/pull/10761
|
||||
[10733]: https://github.com/enso-org/enso/pull/10733
|
||||
[10725]: https://github.com/enso-org/enso/pull/10725
|
||||
|
||||
# Enso 2023.3
|
||||
|
||||
|
@ -80,7 +80,7 @@ polyglot java import org.enso.base.numeric.Decimal_Utils
|
||||
named methods, but which cannot take a `Math_Context`.
|
||||
|
||||
In the case of `divide`, it is possible that the result will have a
|
||||
non-terminating deicmal expansion. If the operation did not specify a
|
||||
non-terminating decimal expansion. If the operation did not specify a
|
||||
`Math_Context`, or specified an explicit `Math_Context` with infinite
|
||||
precision, then it is impossible to represent the result as requested, and an
|
||||
`Arithmetic_Error` will be thrown. In this case, the solution is to specify
|
||||
@ -89,6 +89,9 @@ type Decimal
|
||||
## PRIVATE
|
||||
Value (big_decimal : BigDecimal)
|
||||
|
||||
## PRIVATE
|
||||
From_Float (big_decimal : BigDecimal) (original_value : Float)
|
||||
|
||||
## ICON input_number
|
||||
Construct a `Decimal` from a `Text`, `Integer` or `Float`.
|
||||
|
||||
@ -103,35 +106,68 @@ type Decimal
|
||||
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
|
||||
? Creating `Decimal`s and Converting to `Decimal`
|
||||
|
||||
- If the `Text` argument is incorrectly formatted, a `Number_Parse_Error`
|
||||
When creating a `Decimal` from a literal floating-point value, the
|
||||
preferred method is to express the literal as a string and use
|
||||
`Decimal.from_text`, since this will give a `Decimal` that matches the
|
||||
value precisely.
|
||||
|
||||
To convert a `Float` or `Integer` to a `Decimal`, use its `.to_decimal`
|
||||
method.
|
||||
|
||||
You can also use the convenience method `dec` to convert any `Integer`,
|
||||
`Float`, or `Text` value to a `Decimal`. `dec` does not attach a
|
||||
warning.
|
||||
|
||||
! Error and Warning Conditions in creating and using `Decimal`s
|
||||
|
||||
- If a `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.
|
||||
`Loss_Of_Numeric_Precision` warning is attached. This can happen in
|
||||
the followoing ways:
|
||||
- A `Float` value is implicitly converted to a `Decimal` (either by
|
||||
calling `Decimal.from` or by passing the `Float` as a parameter of
|
||||
type `Decimal`)
|
||||
- A `Math_Context` value is explicitly passed, and the precision it
|
||||
specifies is not sufficient to precisely represent the argument to
|
||||
be converted
|
||||
- If an arithmetic operation is used on mixed arguments (a `Decimal`
|
||||
and a `Float`), an `Illegal_Argument` error is thrown.
|
||||
- If an arithmetic operation is used on a `Decimal` that was implicitly
|
||||
converted from a `Float` (either by calling `Decimal.from` or by
|
||||
passing the `Float` as a parameter of type `Decimal`), an
|
||||
`Illegal_Argument` error is thrown.
|
||||
- If a floating-poing argument is `NaN` or `+/- Inf`, an
|
||||
`Illegal_Argument` error is thrown.
|
||||
|
||||
^ Example
|
||||
Create a `Decimal` from a `Text`.
|
||||
|
||||
c = Decimal.new "12.345"
|
||||
c = Decimal.from_text "12.345"
|
||||
|
||||
^ Example
|
||||
Create a `Decimal` from an `Integer`.
|
||||
|
||||
c = Decimal.new 12345
|
||||
c = 12345.to_decimal
|
||||
|
||||
^ Example
|
||||
Create a `Decimal` from a `Float`.
|
||||
|
||||
c = Decimal.new 12.345
|
||||
c = 12.345.to_decimal
|
||||
|
||||
^ Example
|
||||
Create a `Decimal` from a value.
|
||||
|
||||
c = dec 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_text x mc
|
||||
_ : Integer -> Decimal.from_integer x mc
|
||||
_ : Float -> Decimal.from_float x mc
|
||||
_ : Float -> Decimal.from_float x mc explicit=False
|
||||
|
||||
## GROUP Conversions
|
||||
ICON convert
|
||||
@ -148,17 +184,51 @@ type Decimal
|
||||
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
|
||||
? Creating `Decimal`s and Converting to `Decimal`
|
||||
|
||||
- If `s` is incorrectly formatted, a `Number_Parse_Error` is thrown.
|
||||
When creating a `Decimal` from a literal floating-point value, the
|
||||
preferred method is to express the literal as a string and use
|
||||
`Decimal.from_text`, since this will give a `Decimal` that matches the
|
||||
value precisely.
|
||||
|
||||
To convert a `Float` or `Integer` to a `Decimal`, use its `.to_decimal`
|
||||
method.
|
||||
|
||||
You can also use the convenience method `dec` to convert any `Integer`,
|
||||
`Float`, or `Text` value to a `Decimal`. `dec` does not attach a
|
||||
warning.
|
||||
|
||||
! Error and Warning Conditions in creating and using `Decimal`s
|
||||
|
||||
- If a `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.
|
||||
`Loss_Of_Numeric_Precision` warning is attached. This can happen in
|
||||
the followoing ways:
|
||||
- A `Float` value is implicitly converted to a `Decimal` (either by
|
||||
calling `Decimal.from` or by passing the `Float` as a parameter of
|
||||
type `Decimal`)
|
||||
- A `Math_Context` value is explicitly passed, and the precision it
|
||||
specifies is not sufficient to precisely represent the argument to
|
||||
be converted
|
||||
- If an arithmetic operation is used on mixed arguments (a `Decimal`
|
||||
and a `Float`), an `Illegal_Argument` error is thrown.
|
||||
- If an arithmetic operation is used on a `Decimal` that was implicitly
|
||||
converted from a `Float` (either by calling `Decimal.from` or by
|
||||
passing the `Float` as a parameter of type `Decimal`), an
|
||||
`Illegal_Argument` error is thrown.
|
||||
- If a floating-poing argument is `NaN` or `+/- Inf`, an
|
||||
`Illegal_Argument` error is thrown.
|
||||
|
||||
^ Example
|
||||
Create a `Decimal` from a `Text`.
|
||||
|
||||
d = Decimal.from_text "12.345"
|
||||
|
||||
^ Example
|
||||
Create a `Decimal` from a `Text`.
|
||||
|
||||
d = dec "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 <|
|
||||
@ -176,16 +246,51 @@ type Decimal
|
||||
If a `Math_Context` is used, there is a possibility of a loss of
|
||||
precision.
|
||||
|
||||
! Error Conditions
|
||||
? Creating `Decimal`s and Converting to `Decimal`
|
||||
|
||||
When creating a `Decimal` from a literal floating-point value, the
|
||||
preferred method is to express the literal as a string and use
|
||||
`Decimal.from_text`, since this will give a `Decimal` that matches the
|
||||
value precisely.
|
||||
|
||||
To convert a `Float` or `Integer` to a `Decimal`, use its `.to_decimal`
|
||||
method.
|
||||
|
||||
You can also use the convenience method `dec` to convert any `Integer`,
|
||||
`Float`, or `Text` value to a `Decimal`. `dec` does not attach a
|
||||
warning.
|
||||
|
||||
! Error and Warning Conditions in creating and using `Decimal`s
|
||||
|
||||
- If a `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.
|
||||
`Loss_Of_Numeric_Precision` warning is attached. This can happen in
|
||||
the followoing ways:
|
||||
- A `Float` value is implicitly converted to a `Decimal` (either by
|
||||
calling `Decimal.from` or by passing the `Float` as a parameter of
|
||||
type `Decimal`)
|
||||
- A `Math_Context` value is explicitly passed, and the precision it
|
||||
specifies is not sufficient to precisely represent the argument to
|
||||
be converted
|
||||
- If an arithmetic operation is used on mixed arguments (a `Decimal`
|
||||
and a `Float`), an `Illegal_Argument` error is thrown.
|
||||
- If an arithmetic operation is used on a `Decimal` that was implicitly
|
||||
converted from a `Float` (either by calling `Decimal.from` or by
|
||||
passing the `Float` as a parameter of type `Decimal`), an
|
||||
`Illegal_Argument` error is thrown.
|
||||
- If a floating-poing argument is `NaN` or `+/- Inf`, an
|
||||
`Illegal_Argument` error is thrown.
|
||||
|
||||
^ Example
|
||||
Create a `Decimal` from an `Integer`.
|
||||
|
||||
d = Decimal.from_integer 12
|
||||
c = 12345.to_decimal
|
||||
|
||||
^ Example
|
||||
Create a `Decimal` from an `Integer`.
|
||||
|
||||
c = dec 12345
|
||||
from_integer : Integer -> Math_Context | Nothing -> Decimal
|
||||
from_integer (i : Integer) (mc : Math_Context | Nothing = Nothing) -> Decimal =
|
||||
handle_java_exception <|
|
||||
@ -211,24 +316,61 @@ type Decimal
|
||||
a `Float` always attaches a `Loss_Of_Numeric_Precision` warning to the
|
||||
result.
|
||||
|
||||
! Error Conditions
|
||||
? Creating `Decimal`s and Converting to `Decimal`
|
||||
|
||||
- A `Loss_Of_Numeric_Precision` warning is always attached when
|
||||
converting to `Decimal` from `Float`.
|
||||
- If `f` is NaN or +/-Inf, an Illegal_Argument error is thrown.
|
||||
When creating a `Decimal` from a literal floating-point value, the
|
||||
preferred method is to express the literal as a string and use
|
||||
`Decimal.from_text`, since this will give a `Decimal` that matches the
|
||||
value precisely.
|
||||
|
||||
To convert a `Float` or `Integer` to a `Decimal`, use its `.to_decimal`
|
||||
method.
|
||||
|
||||
You can also use the convenience method `dec` to convert any `Integer`,
|
||||
`Float`, or `Text` value to a `Decimal`. `dec` does not attach a
|
||||
warning.
|
||||
|
||||
! Error and Warning Conditions in creating and using `Decimal`s
|
||||
|
||||
- If a `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 happen in
|
||||
the followoing ways:
|
||||
- A `Float` value is implicitly converted to a `Decimal` (either by
|
||||
calling `Decimal.from` or by passing the `Float` as a parameter of
|
||||
type `Decimal`)
|
||||
- A `Math_Context` value is explicitly passed, and the precision it
|
||||
specifies is not sufficient to precisely represent the argument to
|
||||
be converted
|
||||
- If an arithmetic operation is used on mixed arguments (a `Decimal`
|
||||
and a `Float`), an `Illegal_Argument` error is thrown.
|
||||
- If an arithmetic operation is used on a `Decimal` that was implicitly
|
||||
converted from a `Float` (either by calling `Decimal.from` or by
|
||||
passing the `Float` as a parameter of type `Decimal`), an
|
||||
`Illegal_Argument` error is thrown.
|
||||
- If a floating-poing argument is `NaN` or `+/- Inf`, an
|
||||
`Illegal_Argument` error is thrown.
|
||||
|
||||
^ Example
|
||||
Create a `Decimal` from a `Float`.
|
||||
|
||||
d = Decimal.from_integer 12.345
|
||||
from_float : Float -> Math_Context | Nothing -> Decimal ! Arithmetic_Error | Illegal_Argument
|
||||
from_float (f : Float) (mc : Math_Context | Nothing = Nothing) -> Decimal ! Arithmetic_Error | Illegal_Argument =
|
||||
c = 12.345.to_decimal
|
||||
|
||||
^ Example
|
||||
Create a `Decimal` from a `Float`.
|
||||
|
||||
c = dec 12.345
|
||||
from_float : Float -> Math_Context | Nothing -> Boolean -> Decimal ! Arithmetic_Error | Illegal_Argument
|
||||
from_float (f : Float) (mc : Math_Context | Nothing = Nothing) (explicit : Boolean = True) -> Decimal ! Arithmetic_Error | Illegal_Argument =
|
||||
is_exceptional = f.is_nan || f.is_infinite
|
||||
if is_exceptional then Error.throw (Illegal_Argument.Error "Cannot convert "+f.to_text+" to a Decimal") else
|
||||
handle_java_exception <| attach_loss_of_numeric_precision f <|
|
||||
case mc of
|
||||
_ : Math_Context -> Decimal.Value <| handle_precision_loss f <| Decimal_Utils.fromFloat f mc.math_context
|
||||
_ : Nothing -> Decimal.Value (Decimal_Utils.fromFloat f)
|
||||
handle_java_exception <|
|
||||
big_decimal = case mc of
|
||||
_ : Math_Context -> handle_precision_loss f <| Decimal_Utils.fromFloat f mc.math_context
|
||||
_ : Nothing -> Decimal_Utils.fromFloat f
|
||||
if explicit then Decimal.Value big_decimal else
|
||||
attach_loss_of_numeric_precision f (Decimal.From_Float big_decimal f)
|
||||
|
||||
## ALIAS greater than
|
||||
GROUP Operators
|
||||
@ -328,9 +470,10 @@ type Decimal
|
||||
b = Decimal.new "20.33"
|
||||
a.add b (Math_Context.new 3)
|
||||
# => Decimal.new 30.5
|
||||
# TODO: restore checked return type (here and elsewhere) after https://github.com/enso-org/enso/issues/10736
|
||||
add : Decimal -> Math_Context | Nothing -> Decimal ! Arithmetic_Error
|
||||
add self (that : Decimal) (math_context : Math_Context | Nothing = Nothing) -> Decimal ! Arithmetic_Error =
|
||||
handle_java_exception <|
|
||||
error_if_from_float self that <| handle_java_exception <|
|
||||
case math_context of
|
||||
Nothing -> Decimal.Value (self.big_decimal.add that.big_decimal)
|
||||
_ -> Decimal.Value (self.big_decimal.add that.big_decimal math_context.math_context)
|
||||
@ -388,7 +531,7 @@ type Decimal
|
||||
# => Decimal.new 10.1
|
||||
subtract : Decimal -> Math_Context | Nothing -> Decimal ! Arithmetic_Error
|
||||
subtract self (that : Decimal) (math_context : Math_Context | Nothing = Nothing) -> Decimal ! Arithmetic_Error =
|
||||
handle_java_exception <|
|
||||
error_if_from_float self that <| handle_java_exception <|
|
||||
case math_context of
|
||||
Nothing -> Decimal.Value (self.big_decimal.subtract that.big_decimal)
|
||||
_ -> Decimal.Value (self.big_decimal.subtract that.big_decimal math_context.math_context)
|
||||
@ -447,7 +590,7 @@ type Decimal
|
||||
# => Decimal.new 207.8
|
||||
multiply : Decimal -> Math_Context | Nothing -> Decimal ! Arithmetic_Error
|
||||
multiply self (that : Decimal) (math_context : Math_Context | Nothing = Nothing) -> Decimal ! Arithmetic_Error =
|
||||
handle_java_exception <|
|
||||
error_if_from_float self that <| handle_java_exception <|
|
||||
case math_context of
|
||||
Nothing -> Decimal.Value (self.big_decimal.multiply that.big_decimal)
|
||||
_ -> Decimal.Value (self.big_decimal.multiply that.big_decimal math_context.math_context)
|
||||
@ -511,7 +654,7 @@ type Decimal
|
||||
divide : Decimal -> Math_Context | Nothing -> Decimal ! Arithmetic_Error
|
||||
divide self (that : Decimal) (math_context : Math_Context | Nothing = Nothing) -> Decimal ! Arithmetic_Error =
|
||||
extra_message = " Please use `.divide` with an explicit `Math_Context` to limit the numeric precision."
|
||||
handle_java_exception extra_message=extra_message <|
|
||||
error_if_from_float self that <| handle_java_exception extra_message=extra_message <|
|
||||
case math_context of
|
||||
Nothing -> Decimal.Value (self.big_decimal.divide that.big_decimal)
|
||||
_ -> Decimal.Value (self.big_decimal.divide that.big_decimal math_context.math_context)
|
||||
@ -566,7 +709,7 @@ type Decimal
|
||||
# => -2
|
||||
remainder : Decimal -> Decimal
|
||||
remainder self (that : Decimal) -> Decimal =
|
||||
handle_java_exception <|
|
||||
error_if_from_float self that <| handle_java_exception <|
|
||||
Decimal.Value (self.big_decimal.remainder that.big_decimal)
|
||||
|
||||
## ALIAS modulo, modulus, remainder
|
||||
@ -628,7 +771,7 @@ type Decimal
|
||||
# => 3
|
||||
div : Decimal -> Decimal
|
||||
div self that:Decimal -> Decimal =
|
||||
handle_java_exception <|
|
||||
error_if_from_float self that <| handle_java_exception <|
|
||||
Decimal.Value (self.big_decimal.divideToIntegralValue that.big_decimal)
|
||||
|
||||
## ALIAS power
|
||||
@ -732,7 +875,9 @@ type Decimal
|
||||
Decimal.new "12" . min (Decimal.new "13")
|
||||
# => Decimal.new "12"
|
||||
min : Decimal -> Decimal
|
||||
min self (that : Decimal) -> Decimal = if self < that then self else that
|
||||
min self (that : Decimal) -> Decimal =
|
||||
handle_java_exception <|
|
||||
if self < that then self else that
|
||||
|
||||
## GROUP Math
|
||||
ICON transform4
|
||||
@ -747,7 +892,9 @@ type Decimal
|
||||
Decimal.new "12" . max (Decimal.new "13")
|
||||
# => Decimal.new "13"
|
||||
max : Decimal -> Decimal
|
||||
max self (that : Decimal) -> Decimal = if self > that then self else that
|
||||
max self (that : Decimal) -> Decimal =
|
||||
handle_java_exception <|
|
||||
if self > that then self else that
|
||||
|
||||
## GROUP Conversions
|
||||
ICON convert
|
||||
@ -769,7 +916,7 @@ type Decimal
|
||||
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
|
||||
attach_loss_of_numeric_precision self as_biginteger
|
||||
|
||||
## GROUP Conversions
|
||||
ICON convert
|
||||
@ -806,7 +953,7 @@ type Decimal
|
||||
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
|
||||
if f.is_finite then f else
|
||||
message = "Outside representable Float range (approximately (-1.8E308, 1.8E308))"
|
||||
Warning.attach (Out_Of_Range.Error self message) f
|
||||
|
||||
@ -1081,7 +1228,11 @@ type Decimal
|
||||
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
|
||||
handle_java_exception <|
|
||||
case x of
|
||||
_ : Text -> Decimal.from_text x mc
|
||||
_ : Integer -> Decimal.from_integer x mc
|
||||
_ : Float -> Decimal.from_float x mc explicit=True
|
||||
|
||||
## PRIVATE
|
||||
handle_number_format_exception ~action =
|
||||
@ -1092,13 +1243,12 @@ handle_number_format_exception ~action =
|
||||
handle_precision_loss : Any -> ConversionResult -> Any
|
||||
handle_precision_loss original_value conversion_result:ConversionResult -> Any =
|
||||
if conversion_result.hasPrecisionLoss.not then conversion_result.newValue else
|
||||
new_value = conversion_result.newValue
|
||||
Warning.attach (Loss_Of_Numeric_Precision.Warning original_value new_value) new_value
|
||||
attach_loss_of_numeric_precision original_value conversion_result.newValue
|
||||
|
||||
## PRIVATE
|
||||
attach_loss_of_numeric_precision : Float -> Any -> Any
|
||||
attach_loss_of_numeric_precision x value =
|
||||
Warning.attach (Loss_Of_Numeric_Precision.Warning x value) value
|
||||
attach_loss_of_numeric_precision orig value =
|
||||
Warning.attach (Loss_Of_Numeric_Precision.Warning orig value) value
|
||||
|
||||
## PRIVATE
|
||||
handle_java_exception ~action (extra_message : Text = "") =
|
||||
@ -1123,11 +1273,25 @@ Decimal.from (that : Text) = Decimal.from_text that
|
||||
Decimal.from (that : Integer) = Decimal.new that
|
||||
|
||||
## PRIVATE
|
||||
Decimal.from (that : Float) =
|
||||
is_exceptional = that.is_nan || that.is_infinite
|
||||
if is_exceptional then Error.throw (Illegal_Argument.Error "Cannot convert "+that.to_text+" to a Decimal") else
|
||||
handle_java_exception <| attach_loss_of_numeric_precision that <|
|
||||
Decimal.Value <| Decimal_Utils.fromFloat that
|
||||
Decimal.from (that : Float) = Decimal.from_float that explicit=False
|
||||
|
||||
## PRIVATE
|
||||
Float.from (that : Decimal) = that.to_float
|
||||
Float.from (that : Decimal) =
|
||||
attach_loss_of_numeric_precision that that.to_float
|
||||
|
||||
## PRIVATE
|
||||
Helper method allowing access to the backing field.
|
||||
get_big_decimal (that : Decimal) = that.big_decimal
|
||||
|
||||
## PRIVATE
|
||||
Create a Decimal from the Java backing field.
|
||||
from_big_decimal that = Decimal.Value that
|
||||
|
||||
## PRIVATE
|
||||
error_if_from_float left right ~action =
|
||||
msg = "Cannot perform operation on Float and Decimal; please convert the Float to Decimal using .to_decimal."
|
||||
case left of
|
||||
Decimal.From_Float _ _ -> Error.throw (Illegal_Argument.Error msg)
|
||||
_ -> case right of
|
||||
Decimal.From_Float _ _ -> Error.throw (Illegal_Argument.Error msg)
|
||||
_ -> action
|
||||
|
@ -3,6 +3,8 @@ private
|
||||
import project.Data.Decimal.Decimal
|
||||
import project.Data.Numbers.Number
|
||||
import project.Data.Text.Text
|
||||
from project.Errors.Common import Loss_Of_Numeric_Precision
|
||||
import project.Warning.Warning
|
||||
from project.Data.Ordering import Comparable, Ordering
|
||||
|
||||
polyglot java import org.enso.base.numeric.Decimal_Utils
|
||||
@ -11,4 +13,5 @@ polyglot java import org.enso.base.numeric.Decimal_Utils
|
||||
type Decimal_Comparator
|
||||
compare (x : Decimal) (y : Decimal) =
|
||||
Ordering.from_sign (x.big_decimal.compareTo y.big_decimal)
|
||||
|
||||
hash x:Decimal = Decimal_Utils.hashCodeOf x.big_decimal
|
||||
|
@ -2,6 +2,7 @@ from Standard.Base import all
|
||||
import Standard.Base.Errors.Common.Index_Out_Of_Bounds
|
||||
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
|
||||
import Standard.Base.Errors.Illegal_State.Illegal_State
|
||||
from Standard.Base.Data.Decimal import from_big_decimal, get_big_decimal
|
||||
|
||||
import project.Column.Column
|
||||
import project.Value_Type.Bits
|
||||
@ -78,7 +79,7 @@ closest_storage_type value_type = case value_type of
|
||||
This step is unnecessary for primitive and builtin values, but necessary for
|
||||
values such as `Decimal`/`BigDecimal`.
|
||||
enso_to_java x = case x of
|
||||
Decimal.Value big_decimal -> big_decimal
|
||||
_ : Decimal -> get_big_decimal x
|
||||
_ -> x
|
||||
|
||||
## PRIVATE
|
||||
@ -88,7 +89,7 @@ enso_to_java x = case x of
|
||||
necessary for values such as `Decimal`/`BigDecimal`.
|
||||
java_to_enso x = case x of
|
||||
_ : Nothing -> Nothing
|
||||
_ : BigDecimal -> Decimal.Value x
|
||||
_ : BigDecimal -> from_big_decimal x
|
||||
_ -> x
|
||||
|
||||
## PRIVATE
|
||||
|
@ -9,6 +9,7 @@ import com.oracle.truffle.api.dsl.GenerateUncached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
||||
import com.oracle.truffle.api.library.CachedLibrary;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
@ -27,6 +28,7 @@ import org.enso.interpreter.runtime.data.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.data.atom.StructsLibrary;
|
||||
import org.enso.interpreter.runtime.library.dispatch.TypeOfNode;
|
||||
import org.enso.interpreter.runtime.state.State;
|
||||
import org.enso.interpreter.runtime.warning.WarningsLibrary;
|
||||
|
||||
@GenerateUncached
|
||||
public abstract class EqualsAtomNode extends Node {
|
||||
@ -91,16 +93,24 @@ public abstract class EqualsAtomNode extends Node {
|
||||
@Cached(value = "customComparatorNode.execute(self)") Type cachedComparator,
|
||||
@Cached(value = "findCompareMethod(cachedComparator)", allowUncached = true)
|
||||
Function compareFn,
|
||||
@Cached(value = "invokeCompareNode(compareFn)") InvokeFunctionNode invokeNode) {
|
||||
var otherComparator = customComparatorNode.execute(other);
|
||||
if (cachedComparator != otherComparator) {
|
||||
return false;
|
||||
@Cached(value = "invokeCompareNode(compareFn)") InvokeFunctionNode invokeNode,
|
||||
@Shared @CachedLibrary(limit = "10") WarningsLibrary warnings) {
|
||||
try {
|
||||
var otherComparator = customComparatorNode.execute(other);
|
||||
if (cachedComparator != otherComparator) {
|
||||
return false;
|
||||
}
|
||||
var ctx = EnsoContext.get(this);
|
||||
var args = new Object[] {cachedComparator, self, other};
|
||||
var result = invokeNode.execute(compareFn, null, State.create(ctx), args);
|
||||
assert orderingOrNullOrError(this, ctx, result, compareFn);
|
||||
if (warnings.hasWarnings(result)) {
|
||||
result = warnings.removeWarnings(result);
|
||||
}
|
||||
return ctx.getBuiltins().ordering().newEqual() == result;
|
||||
} catch (UnsupportedMessageException e) {
|
||||
throw EnsoContext.get(this).raiseAssertionPanic(this, null, e);
|
||||
}
|
||||
var ctx = EnsoContext.get(this);
|
||||
var args = new Object[] {cachedComparator, self, other};
|
||||
var result = invokeNode.execute(compareFn, null, State.create(ctx), args);
|
||||
assert orderingOrNullOrError(this, ctx, result, compareFn);
|
||||
return ctx.getBuiltins().ordering().newEqual() == result;
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
@ -123,16 +133,21 @@ public abstract class EqualsAtomNode extends Node {
|
||||
|
||||
@Specialization(
|
||||
replaces = {"equalsAtomsWithDefaultComparator", "equalsAtomsWithCustomComparator"})
|
||||
boolean equalsAtomsUncached(VirtualFrame frame, Atom self, Atom other) {
|
||||
boolean equalsAtomsUncached(
|
||||
VirtualFrame frame,
|
||||
Atom self,
|
||||
Atom other,
|
||||
@Shared @CachedLibrary(limit = "10") WarningsLibrary warnings) {
|
||||
if (self.getConstructor() != other.getConstructor()) {
|
||||
return false;
|
||||
} else {
|
||||
return equalsAtomsUncached(frame == null ? null : frame.materialize(), self, other);
|
||||
return equalsAtomsUncached(frame == null ? null : frame.materialize(), self, other, warnings);
|
||||
}
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private boolean equalsAtomsUncached(MaterializedFrame frame, Atom self, Atom other) {
|
||||
private boolean equalsAtomsUncached(
|
||||
MaterializedFrame frame, Atom self, Atom other, WarningsLibrary warnings) {
|
||||
Type customComparator = CustomComparatorNode.getUncached().execute(self);
|
||||
if (customComparator != null) {
|
||||
Function compareFunc = findCompareMethod(customComparator);
|
||||
@ -144,7 +159,8 @@ public abstract class EqualsAtomNode extends Node {
|
||||
CustomComparatorNode.getUncached(),
|
||||
customComparator,
|
||||
compareFunc,
|
||||
invokeFuncNode);
|
||||
invokeFuncNode,
|
||||
warnings);
|
||||
}
|
||||
for (int i = 0; i < self.getConstructor().getArity(); i++) {
|
||||
var selfField = StructsLibrary.getUncached().getField(self, i);
|
||||
|
@ -1,5 +1,6 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Errors.Common.Incomparable_Values
|
||||
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
|
||||
|
||||
from Standard.Test import all
|
||||
|
||||
@ -34,6 +35,19 @@ type No_Ord_Comparator
|
||||
|
||||
Comparable.from (that:No_Ord) = Comparable.new that No_Ord_Comparator
|
||||
|
||||
type Attach_Warning
|
||||
Value (n : Integer)
|
||||
Value2 (n : Integer)
|
||||
|
||||
type Attach_Warning_Comparator
|
||||
compare (x : Attach_Warning) (y : Attach_Warning) =
|
||||
r = Ordering.compare x.n y.n
|
||||
Warning.attach (Illegal_Argument.Error "warning") r
|
||||
|
||||
hash x:Attach_Warning = Ordering.hash x.n
|
||||
|
||||
Comparable.from (that : Attach_Warning) = Comparable.new that Attach_Warning_Comparator
|
||||
|
||||
# Tests
|
||||
|
||||
add_specs suite_builder = suite_builder.group "Object Comparator" group_builder->
|
||||
@ -79,6 +93,11 @@ add_specs suite_builder = suite_builder.group "Object Comparator" group_builder-
|
||||
(default_comparator 1 True) . should_fail_with Incomparable_Values
|
||||
(default_comparator (No_Ord.Value 1) (No_Ord.Value 2)).should_fail_with Incomparable_Values
|
||||
|
||||
group_builder.specify "warnings attached to the return value of .compare should not affect the boolean value of the comparison operator" <|
|
||||
((Attach_Warning.Value 1) < (Attach_Warning.Value 2)) . should_be_true
|
||||
((Attach_Warning.Value 1) == (Attach_Warning.Value 1)) . should_be_true
|
||||
((Attach_Warning.Value 1) == (Attach_Warning.Value2 1)) . should_be_true
|
||||
|
||||
main filter=Nothing =
|
||||
suite = Test.build suite_builder->
|
||||
add_specs suite_builder
|
||||
|
27
test/Base_Internal_Tests/src/Decimal_Constructor_Spec.enso
Normal file
27
test/Base_Internal_Tests/src/Decimal_Constructor_Spec.enso
Normal file
@ -0,0 +1,27 @@
|
||||
from Standard.Base import all
|
||||
|
||||
from Standard.Test import all
|
||||
|
||||
id_decimal (x : Decimal) -> Decimal = x
|
||||
|
||||
is_value_constructor d = case d of
|
||||
Decimal.Value _ -> True
|
||||
Decimal.From_Float _ _ -> False
|
||||
|
||||
add_specs suite_builder =
|
||||
suite_builder.group "(Decimal_Constructor_Spec) conversions" group_builder->
|
||||
group_builder.specify "Conversion from float to decimal should use the correct constructor" <|
|
||||
is_value_constructor (dec 0.1) . should_be_true
|
||||
is_value_constructor (0.1 . to_decimal) . should_be_true
|
||||
is_value_constructor (0.1 . to_decimal) . should_be_true
|
||||
is_value_constructor (Decimal.from_float 0.1) . should_be_true
|
||||
|
||||
is_value_constructor (Decimal.new 0.1) . should_be_false
|
||||
is_value_constructor (Decimal.from 0.1) . should_be_false
|
||||
is_value_constructor (Decimal.from_float 0.1 explicit=False) . should_be_false
|
||||
is_value_constructor (id_decimal 0.1) . should_be_false
|
||||
|
||||
main filter=Nothing =
|
||||
suite = Test.build suite_builder->
|
||||
add_specs suite_builder
|
||||
suite.run_with_filter filter
|
@ -4,12 +4,14 @@ from Standard.Test import all
|
||||
|
||||
import project.Input_Output_Spec
|
||||
import project.Comparator_Spec
|
||||
import project.Decimal_Constructor_Spec
|
||||
import project.Grapheme_Spec
|
||||
|
||||
main filter=Nothing =
|
||||
suite = Test.build suite_builder->
|
||||
Input_Output_Spec.add_specs suite_builder
|
||||
Comparator_Spec.add_specs suite_builder
|
||||
Decimal_Constructor_Spec.add_specs suite_builder
|
||||
Grapheme_Spec.add_specs suite_builder
|
||||
Input_Output_Spec.add_specs suite_builder
|
||||
|
||||
suite.run_with_filter filter
|
||||
|
@ -13,6 +13,8 @@ polyglot java import java.lang.Integer as Java_Integer
|
||||
|
||||
Decimal.should_have_rep self rep = self.internal_representation . should_equal rep
|
||||
|
||||
id_decimal (x : Decimal) -> Decimal = x
|
||||
|
||||
add_specs suite_builder =
|
||||
suite_builder.group "(Decimal_Spec) construction" group_builder->
|
||||
group_builder.specify "should be able to construct a Decimal with dec" <|
|
||||
@ -211,7 +213,7 @@ add_specs suite_builder =
|
||||
|
||||
(Ordering.hash x0) . should_equal (Ordering.hash x1)
|
||||
|
||||
group_builder.specify "should compare correctly to Integer and Float" pending="https://github.com/enso-org/enso/issues/10163" <|
|
||||
group_builder.specify "should compare correctly to Integer and Float" <|
|
||||
values = []
|
||||
+ [[0.1, 0.1]]
|
||||
+ [["0.1", 0.1]]
|
||||
@ -373,6 +375,13 @@ add_specs suite_builder =
|
||||
(lesser > d) . should_be_false
|
||||
(lesser >= d) . should_be_false
|
||||
|
||||
group_builder.specify "mixed float/decimal comparisons should attach a warning" pending="https://github.com/enso-org/enso/issues/10679" <|
|
||||
[(_ == _), (_ != _), (_ <= _), (_ >= _), (_ < _), (_ > _), .min, .max].map c->
|
||||
a = c (Decimal.new "0.1") 0.1
|
||||
b = c 0.1 (Decimal.new "0.1")
|
||||
Problems.expect_only_warning (Loss_Of_Numeric_Precision.Warning 0.1 (Decimal.new "0.1")) a
|
||||
Problems.expect_only_warning (Loss_Of_Numeric_Precision.Warning 0.1 (Decimal.new "0.1")) b
|
||||
|
||||
group_builder.specify "should not implicitly convert Decimal to Integer in comparison" <|
|
||||
(Decimal.new "12.5" == 12) . should_be_false
|
||||
(12 == Decimal.new "12.5") . should_be_false
|
||||
@ -383,173 +392,173 @@ add_specs suite_builder =
|
||||
|
||||
suite_builder.group "(Decimal_Spec) edge cases" group_builder->
|
||||
group_builder.specify "can support values outside the double range" <|
|
||||
d = Decimal.new Float.max_value
|
||||
d = Float.max_value . to_decimal
|
||||
(d == Float.max_value) . should_be_true
|
||||
((d * d) == Float.max_value) . should_be_false
|
||||
((-(d * d)) == -Float.max_value) . should_be_false
|
||||
Ordering.hash (d * d) . should_equal 2146435072
|
||||
Ordering.hash -(d * d) . should_equal -1048576
|
||||
Ordering.hash ((d * d) + 0.1) . should_equal 2146435072
|
||||
Ordering.hash ((-(d * d)) + 0.1) . should_equal -1048576
|
||||
Ordering.hash ((d * d) + 0.1.to_decimal) . should_equal 2146435072
|
||||
Ordering.hash ((-(d * d)) + 0.1.to_decimal) . should_equal -1048576
|
||||
|
||||
group_builder.specify "from conversion supports values outside the double range" <|
|
||||
d = Float.max_value.to Decimal
|
||||
d = Float.max_value . to_decimal
|
||||
(d == Float.max_value) . should_be_true
|
||||
((d * d) == Float.max_value) . should_be_false
|
||||
((-(d * d)) == -Float.max_value) . should_be_false
|
||||
Ordering.hash (d * d) . should_equal 2146435072
|
||||
Ordering.hash -(d * d) . should_equal -1048576
|
||||
Ordering.hash ((d * d) + 0.1) . should_equal 2146435072
|
||||
Ordering.hash ((-(d * d)) + 0.1) . should_equal -1048576
|
||||
Ordering.hash ((d * d) + 0.1.to_decimal) . should_equal 2146435072
|
||||
Ordering.hash ((-(d * d)) + 0.1.to_decimal) . should_equal -1048576
|
||||
|
||||
suite_builder.group "(Decimal_Spec) arithmetic" group_builder->
|
||||
group_builder.specify "should allow arithmetic with Decimals, without Math_Context" <|
|
||||
(Decimal.new 1 + Decimal.new 2) . should_equal (Decimal.new 3)
|
||||
(Decimal.new 1.1 + Decimal.new 2.2) . should_equal (Decimal.new 3.3)
|
||||
(Decimal.from_integer 1 + Decimal.from_integer 2) . should_equal (Decimal.from_integer 3)
|
||||
(Decimal.from_float 1.1 + Decimal.from_float 2.2) . should_equal (Decimal.from_float 3.3)
|
||||
|
||||
(Decimal.new 10 - Decimal.new 6) . should_equal (Decimal.new 4)
|
||||
(Decimal.new 10.1 - Decimal.new 6.6) . should_equal (Decimal.new 3.5)
|
||||
(Decimal.from_integer 10 - Decimal.from_integer 6) . should_equal (Decimal.from_integer 4)
|
||||
(Decimal.from_float 10.1 - Decimal.from_float 6.6) . should_equal (Decimal.from_float 3.5)
|
||||
|
||||
(Decimal.new 3 * Decimal.new 7) . should_equal (Decimal.new 21)
|
||||
(Decimal.new 3.12 * Decimal.new 7.97) . should_equal (Decimal.new 24.8664)
|
||||
(Decimal.from_integer 3 * Decimal.from_integer 7) . should_equal (Decimal.from_integer 21)
|
||||
(Decimal.from_float 3.12 * Decimal.from_float 7.97) . should_equal (Decimal.from_float 24.8664)
|
||||
|
||||
(Decimal.new 50 / Decimal.new 2) . should_equal (Decimal.new 25)
|
||||
(Decimal.new 50.75 / Decimal.new 2.5) . should_equal (Decimal.new 20.3)
|
||||
(Decimal.from_integer 50 / Decimal.from_integer 2) . should_equal (Decimal.from_integer 25)
|
||||
(Decimal.from_float 50.75 / Decimal.from_float 2.5) . should_equal (Decimal.from_float 20.3)
|
||||
|
||||
(Decimal.new 1 + Decimal.new -2) . should_equal (Decimal.new -1)
|
||||
(Decimal.new 1.1 + Decimal.new -2.2) . should_equal (Decimal.new -1.1)
|
||||
(Decimal.from_integer 1 + Decimal.from_integer -2) . should_equal (Decimal.from_integer -1)
|
||||
(Decimal.from_float 1.1 + Decimal.from_float -2.2) . should_equal (Decimal.from_float -1.1)
|
||||
|
||||
(Decimal.new -10 - Decimal.new 6) . should_equal (Decimal.new -16)
|
||||
(Decimal.new 10.1 - Decimal.new -6.6) . should_equal (Decimal.new 16.7)
|
||||
(Decimal.from_integer -10 - Decimal.from_integer 6) . should_equal (Decimal.from_integer -16)
|
||||
(Decimal.from_float 10.1 - Decimal.from_float -6.6) . should_equal (Decimal.from_float 16.7)
|
||||
|
||||
(Decimal.new 3 * Decimal.new -7) . should_equal (Decimal.new -21)
|
||||
(Decimal.new -3.12 * Decimal.new 7.97) . should_equal (Decimal.new -24.8664)
|
||||
(Decimal.from_integer 3 * Decimal.from_integer -7) . should_equal (Decimal.from_integer -21)
|
||||
(Decimal.from_float -3.12 * Decimal.from_float 7.97) . should_equal (Decimal.from_float -24.8664)
|
||||
|
||||
(Decimal.new -50 / Decimal.new -2) . should_equal (Decimal.new 25)
|
||||
(Decimal.new -50.75 / Decimal.new -2.5) . should_equal (Decimal.new 20.3)
|
||||
(Decimal.from_integer -50 / Decimal.from_integer -2) . should_equal (Decimal.from_integer 25)
|
||||
(Decimal.from_float -50.75 / Decimal.from_float -2.5) . should_equal (Decimal.from_float 20.3)
|
||||
|
||||
(Decimal.new 213427523957 + Decimal.new 93849398884384) . should_equal (Decimal.new 94062826408341)
|
||||
(Decimal.from_integer 213427523957 + Decimal.from_integer 93849398884384) . should_equal (Decimal.from_integer 94062826408341)
|
||||
(Decimal.new "723579374753.3535345" + Decimal.new "35263659267.23434535") . should_equal (Decimal.new "758843034020.58787985")
|
||||
|
||||
(Decimal.new -29388920982834 - Decimal.new 842820) . should_equal (Decimal.new -29388921825654)
|
||||
(Decimal.from_integer -29388920982834 - Decimal.from_integer 842820) . should_equal (Decimal.from_integer -29388921825654)
|
||||
(Decimal.new "-8273762787.3535345" - Decimal.new "76287273.23434535") . should_equal (Decimal.new "-8350050060.58787985")
|
||||
|
||||
(Decimal.new 7297927982888383 * Decimal.new 828737) . should_equal (Decimal.new 6048062942754969862271)
|
||||
(Decimal.from_integer 7297927982888383 * Decimal.from_integer 828737) . should_equal (Decimal.from_integer 6048062942754969862271)
|
||||
(Decimal.new "893872388.3535345" * Decimal.new "72374727737.23434535") . should_equal (Decimal.new "64693770738918463976.840471466139575")
|
||||
|
||||
(Decimal.new "909678645268840" / Decimal.new "28029830") . should_equal (Decimal.new "32453948")
|
||||
(Decimal.new "384456406.7860325392609633764" / Decimal.new "24556.125563546") . should_equal (Decimal.new "15656.2323234234")
|
||||
|
||||
(Decimal.new 3948539458034580838458034803485 + Decimal.new 237957498573948579387495837459837) . should_equal (Decimal.new 241906038031983160225953872263322)
|
||||
(Decimal.from_integer 3948539458034580838458034803485 + Decimal.from_integer 237957498573948579387495837459837) . should_equal (Decimal.from_integer 241906038031983160225953872263322)
|
||||
(Decimal.new "927349527347587934.34573495739475938745" + Decimal.new "37593874597239487593745.3457973847574") . should_equal (Decimal.new "37594801946766835181679.69153234215215938745")
|
||||
|
||||
(Decimal.new -239485787538745937495873495759598 - Decimal.new 273958739485793475934793745) . should_equal (Decimal.new -239486061497485423289349430553343)
|
||||
(Decimal.from_integer -239485787538745937495873495759598 - Decimal.from_integer 273958739485793475934793745) . should_equal (Decimal.from_integer -239486061497485423289349430553343)
|
||||
(Decimal.new "-79237492374927979579239292.293749287928373" - Decimal.new "239729379279872947923947.234273947927397239") . should_equal (Decimal.new "-79477221754207852527163239.528023235855770239")
|
||||
|
||||
(Decimal.new 3745937948729837923798237 * Decimal.new 273948729872398729387) . should_equal (Decimal.new 1026194943235357770434981633846801087750690719)
|
||||
(Decimal.from_integer 3745937948729837923798237 * Decimal.from_integer 273948729872398729387) . should_equal (Decimal.from_integer 1026194943235357770434981633846801087750690719)
|
||||
(Decimal.new "38450830483049850394093.48579374987938934" * Decimal.new "297349287397297.2394279379287398729879234") . should_equal (Decimal.new "11433327043969147404767677628296882442.487387236451853113753778864149360386696556")
|
||||
|
||||
(Decimal.new 1026194943235357770434981633846801087750690719 / Decimal.new 273948729872398729387) . should_equal (Decimal.new 3745937948729837923798237)
|
||||
(Decimal.from_integer 1026194943235357770434981633846801087750690719 / Decimal.from_integer 273948729872398729387) . should_equal (Decimal.from_integer 3745937948729837923798237)
|
||||
(Decimal.new "11433327043969147404767677628296882442.487387236451853113753778864149360386696556" / Decimal.new "297349287397297.2394279379287398729879234") . should_equal (Decimal.new "38450830483049850394093.48579374987938934")
|
||||
|
||||
group_builder.specify "should allow arithmetic with Decimals, with precision setting (examples)" <|
|
||||
(Decimal.new "10.22") . add (Decimal.new "20.33") (Math_Context.new 3) . should_equal (Decimal.new 30.6)
|
||||
(Decimal.new "20.33") . subtract (Decimal.new "10.22") (Math_Context.new 3) . should_equal (Decimal.new 10.1)
|
||||
(Decimal.new "10.22") . multiply (Decimal.new "20.33") (Math_Context.new 4) . should_equal (Decimal.new 207.8)
|
||||
(Decimal.new "1065.9378") . divide (Decimal.new "23.34") (Math_Context.new 3) . should_equal (Decimal.new 45.7)
|
||||
(Decimal.new "10.22") . add (Decimal.new "20.33") (Math_Context.new 3) . should_equal (Decimal.new "30.6")
|
||||
(Decimal.new "20.33") . subtract (Decimal.new "10.22") (Math_Context.new 3) . should_equal (Decimal.new "10.1")
|
||||
(Decimal.new "10.22") . multiply (Decimal.new "20.33") (Math_Context.new 4) . should_equal (Decimal.new "207.8")
|
||||
(Decimal.new "1065.9378") . divide (Decimal.new "23.34") (Math_Context.new 3) . should_equal (Decimal.new "45.7")
|
||||
|
||||
group_builder.specify "should allow arithmetic with Decimals, with precision setting" <|
|
||||
(Decimal.new 213427523957 . add (Decimal.new 93849398884384) (Math_Context.new 8)) . should_equal (Decimal.new 94062826000000)
|
||||
(Decimal.from_integer 213427523957 . add (Decimal.from_integer 93849398884384) (Math_Context.new 8)) . should_equal (Decimal.from_integer 94062826000000)
|
||||
(Decimal.new "723579374753.3535345" . add (Decimal.new "35263659267.23434535") (Math_Context.new 12)) . should_equal (Decimal.new "758843034021")
|
||||
|
||||
(Decimal.new -29388920982834 . subtract (Decimal.new 842820) (Math_Context.new 7)) . should_equal (Decimal.new -29388920000000)
|
||||
(Decimal.from_integer -29388920982834 . subtract (Decimal.from_integer 842820) (Math_Context.new 7)) . should_equal (Decimal.from_integer -29388920000000)
|
||||
(Decimal.new "-8273762787.3535345" . subtract (Decimal.new "76287273.23434535") (Math_Context.new 10)) . should_equal (Decimal.new "-8350050061")
|
||||
|
||||
(Decimal.new 7297927982888383 . multiply (Decimal.new 828737) (Math_Context.new 6)) . should_equal (Decimal.new 6048060000000000000000 )
|
||||
(Decimal.from_integer 7297927982888383 . multiply (Decimal.from_integer 828737) (Math_Context.new 6)) . should_equal (Decimal.from_integer 6048060000000000000000 )
|
||||
(Decimal.new "893872388.3535345" . multiply (Decimal.new "72374727737.23434535") (Math_Context.new 14)) . should_equal (Decimal.new "64693770738918000000")
|
||||
|
||||
(Decimal.new "909678645268840" . divide (Decimal.new "28029830") (Math_Context.new 6)) . should_equal (Decimal.new "32453900 ")
|
||||
(Decimal.new "384456406.7860325392609633764" . divide (Decimal.new "24556.125563546") (Math_Context.new 7)) . should_equal (Decimal.new "15656.23")
|
||||
|
||||
(Decimal.new 3948539458034580838458034803485 . add (Decimal.new 237957498573948579387495837459837) (Math_Context.new 20)) . should_equal (Decimal.new 241906038031983160230000000000000)
|
||||
(Decimal.from_integer 3948539458034580838458034803485 . add (Decimal.from_integer 237957498573948579387495837459837) (Math_Context.new 20)) . should_equal (Decimal.from_integer 241906038031983160230000000000000)
|
||||
(Decimal.new "927349527347587934.34573495739475938745" . add (Decimal.new "37593874597239487593745.3457973847574") (Math_Context.new 24)) . should_equal (Decimal.new "37594801946766835181679.7")
|
||||
|
||||
(Decimal.new -239485787538745937495873495759598 . subtract (Decimal.new 273958739485793475934793745) (Math_Context.new 12)) . should_equal (Decimal.new -239486061497000000000000000000000)
|
||||
(Decimal.from_integer -239485787538745937495873495759598 . subtract (Decimal.from_integer 273958739485793475934793745) (Math_Context.new 12)) . should_equal (Decimal.from_integer -239486061497000000000000000000000)
|
||||
(Decimal.new "-79237492374927979579239292.293749287928373" . subtract (Decimal.new "239729379279872947923947.234273947927397239") (Math_Context.new 16)) . should_equal (Decimal.new "-79477221754207850000000000")
|
||||
|
||||
(Decimal.new 3745937948729837923798237 . multiply (Decimal.new 273948729872398729387) (Math_Context.new 21)) . should_equal (Decimal.new 1026194943235357770430000000000000000000000000)
|
||||
(Decimal.from_integer 3745937948729837923798237 . multiply (Decimal.from_integer 273948729872398729387) (Math_Context.new 21)) . should_equal (Decimal.from_integer 1026194943235357770430000000000000000000000000)
|
||||
(Decimal.new "38450830483049850394093.48579374987938934" . multiply (Decimal.new "297349287397297.2394279379287398729879234") (Math_Context.new 35)) . should_equal (Decimal.new "11433327043969147404767677628296882000")
|
||||
|
||||
(Decimal.new 1026194943235357770434981633846801087750690719 . divide (Decimal.new 273948729872398729387) (Math_Context.new 10)) . should_equal (Decimal.new 3745937949000000000000000)
|
||||
(Decimal.from_integer 1026194943235357770434981633846801087750690719 . divide (Decimal.from_integer 273948729872398729387) (Math_Context.new 10)) . should_equal (Decimal.from_integer 3745937949000000000000000)
|
||||
(Decimal.new "11433327043969147404767677628296882442.487387236451853113753778864149360386696556" . divide (Decimal.new "297349287397297.2394279379287398729879234") (Math_Context.new 9)) . should_equal (Decimal.new "38450830500000000000000")
|
||||
|
||||
group_builder.specify "should allow arithmetic with Decimals, with precision setting and rounding mode" <|
|
||||
(Decimal.new 3.4 . add (Decimal.new 20.05) (Math_Context.new 3)) . should_equal (Decimal.new 23.5)
|
||||
(Decimal.new 3.4 . add (Decimal.new 20.05) (Math_Context.new 3 Rounding_Mode.bankers)) . should_equal (Decimal.new 23.4)
|
||||
(Decimal.new -3.4 . add (Decimal.new -20.05) (Math_Context.new 3)) . should_equal (Decimal.new -23.5)
|
||||
(Decimal.new -3.4 . add (Decimal.new -20.05) (Math_Context.new 3 Rounding_Mode.bankers)) . should_equal (Decimal.new -23.4)
|
||||
(Decimal.new -867.625 . subtract (Decimal.new -33.05) (Math_Context.new 5)) . should_equal (Decimal.new -834.58)
|
||||
(Decimal.new -867.625 . subtract (Decimal.new -33.05) (Math_Context.new 5 Rounding_Mode.bankers)) . should_equal (Decimal.new -834.58)
|
||||
(Decimal.new 49.31 . multiply (Decimal.new 5) (Math_Context.new 4)) . should_equal (Decimal.new 246.6)
|
||||
(Decimal.new 49.31 . multiply (Decimal.new 5) (Math_Context.new 4 Rounding_Mode.bankers)) . should_equal (Decimal.new 246.6)
|
||||
(Decimal.new 49.29 . multiply (Decimal.new 5) (Math_Context.new 4)) . should_equal (Decimal.new 246.5)
|
||||
(Decimal.new 49.29 . multiply (Decimal.new 5) (Math_Context.new 4 Rounding_Mode.bankers)) . should_equal (Decimal.new 246.4)
|
||||
(Decimal.new 247.75 . divide (Decimal.new -5) (Math_Context.new 3)) . should_equal (Decimal.new -49.6)
|
||||
(Decimal.new 247.75 . divide (Decimal.new -5) (Math_Context.new 3 Rounding_Mode.bankers)) . should_equal (Decimal.new -49.6)
|
||||
(Decimal.from_float 3.4 . add (Decimal.from_float 20.05) (Math_Context.new 3)) . should_equal (Decimal.from_float 23.5)
|
||||
(Decimal.from_float 3.4 . add (Decimal.from_float 20.05) (Math_Context.new 3 Rounding_Mode.bankers)) . should_equal (Decimal.from_float 23.4)
|
||||
(Decimal.from_float -3.4 . add (Decimal.from_float -20.05) (Math_Context.new 3)) . should_equal (Decimal.from_float -23.5)
|
||||
(Decimal.from_float -3.4 . add (Decimal.from_float -20.05) (Math_Context.new 3 Rounding_Mode.bankers)) . should_equal (Decimal.from_float -23.4)
|
||||
(Decimal.from_float -867.625 . subtract (Decimal.from_float -33.05) (Math_Context.new 5)) . should_equal (Decimal.from_float -834.58)
|
||||
(Decimal.from_float -867.625 . subtract (Decimal.from_float -33.05) (Math_Context.new 5 Rounding_Mode.bankers)) . should_equal (Decimal.from_float -834.58)
|
||||
(Decimal.from_float 49.31 . multiply (Decimal.from_integer 5) (Math_Context.new 4)) . should_equal (Decimal.from_float 246.6)
|
||||
(Decimal.from_float 49.31 . multiply (Decimal.from_integer 5) (Math_Context.new 4 Rounding_Mode.bankers)) . should_equal (Decimal.from_float 246.6)
|
||||
(Decimal.from_float 49.29 . multiply (Decimal.from_integer 5) (Math_Context.new 4)) . should_equal (Decimal.from_float 246.5)
|
||||
(Decimal.from_float 49.29 . multiply (Decimal.from_integer 5) (Math_Context.new 4 Rounding_Mode.bankers)) . should_equal (Decimal.from_float 246.4)
|
||||
(Decimal.from_float 247.75 . divide (Decimal.from_integer -5) (Math_Context.new 3)) . should_equal (Decimal.from_float -49.6)
|
||||
(Decimal.from_float 247.75 . divide (Decimal.from_integer -5) (Math_Context.new 3 Rounding_Mode.bankers)) . should_equal (Decimal.from_float -49.6)
|
||||
|
||||
group_builder.specify "should allow mixed arithmetic" <|
|
||||
(Decimal.new 1 + 2) . should_equal 3
|
||||
(Decimal.new 1 - 2) . should_equal -1
|
||||
(Decimal.new 3 * 4) . should_equal 12
|
||||
(Decimal.new 3 / 4) . should_equal 0.75
|
||||
group_builder.specify "should allow mixed arithmetic (integer)" <|
|
||||
(Decimal.from_integer 1 + 2) . should_equal 3
|
||||
(Decimal.from_integer 1 - 2) . should_equal -1
|
||||
(Decimal.from_integer 3 * 4) . should_equal 12
|
||||
(Decimal.from_integer 3 / 4) . should_equal 0.75
|
||||
|
||||
(1 + Decimal.new 2) . should_equal 3
|
||||
(1 - Decimal.new 2) . should_equal -1
|
||||
(3 * Decimal.new 4) . should_equal 12
|
||||
(3 / Decimal.new 4) . should_equal 0.75
|
||||
(1 + Decimal.from_integer 2) . should_equal 3
|
||||
(1 - Decimal.from_integer 2) . should_equal -1
|
||||
(3 * Decimal.from_integer 4) . should_equal 12
|
||||
(3 / Decimal.from_integer 4) . should_equal 0.75
|
||||
|
||||
(Decimal.new 1 + 2.0) . should_equal 3
|
||||
(Decimal.new 1 - 2.0) . should_equal -1
|
||||
(Decimal.new 3 * 4.0) . should_equal 12
|
||||
(Decimal.new 3 / 4.0) . should_equal 0.75
|
||||
group_builder.specify "should not allow mixed arithmetic (float)" <|
|
||||
(Decimal.from_integer 1 + 2.0) . should_fail_with Illegal_Argument
|
||||
(Decimal.from_integer 1 - 2.0) . should_fail_with Illegal_Argument
|
||||
(Decimal.from_integer 3 * 4.0) . should_fail_with Illegal_Argument
|
||||
(Decimal.from_integer 3 / 4.0) . should_fail_with Illegal_Argument
|
||||
(Decimal.from_integer 3 % 4.0) . should_fail_with Illegal_Argument
|
||||
(Decimal.from_integer 3 . div 4.0) . should_fail_with Illegal_Argument
|
||||
|
||||
(1 + Decimal.new 2.0) . should_equal 3
|
||||
(1 - Decimal.new 2.0) . should_equal -1
|
||||
(3 * Decimal.new 4.0) . should_equal 12
|
||||
(3 / Decimal.new 4.0) . should_equal 0.75
|
||||
(1.0 + Decimal.from_float 2.0) . should_fail_with Illegal_Argument
|
||||
(1.0 - Decimal.from_float 2.0) . should_fail_with Illegal_Argument
|
||||
(3.0 * Decimal.from_float 4.0) . should_fail_with Illegal_Argument
|
||||
(3.0 / Decimal.from_float 4.0) . should_fail_with Illegal_Argument
|
||||
(3.0 % Decimal.from_float 4.0) . should_fail_with Illegal_Argument
|
||||
|
||||
group_builder.specify "Decimal mixed arithmetic should result in Decimal" <|
|
||||
(Decimal.new 1 + 2) . should_be_a Decimal
|
||||
(Decimal.new 1 - 2) . should_be_a Decimal
|
||||
(Decimal.new 3 * 4) . should_be_a Decimal
|
||||
(Decimal.new 3 / 4) . should_be_a Decimal
|
||||
(Decimal.from_integer 1 + 2) . should_be_a Decimal
|
||||
(Decimal.from_integer 1 - 2) . should_be_a Decimal
|
||||
(Decimal.from_integer 3 * 4) . should_be_a Decimal
|
||||
(Decimal.from_integer 3 / 4) . should_be_a Decimal
|
||||
|
||||
(1 + Decimal.new 2) . should_be_a Decimal
|
||||
(1 - Decimal.new 2) . should_be_a Decimal
|
||||
(3 * Decimal.new 4) . should_be_a Decimal
|
||||
(3 / Decimal.new 4) . should_be_a Decimal
|
||||
(1 + Decimal.from_integer 2) . should_be_a Decimal
|
||||
(1 - Decimal.from_integer 2) . should_be_a Decimal
|
||||
(3 * Decimal.from_integer 4) . should_be_a Decimal
|
||||
(3 / Decimal.from_integer 4) . should_be_a Decimal
|
||||
|
||||
group_builder.specify "should get an aritmetic error when a result is a nonterminating decimal expansion" <|
|
||||
(Decimal.new 1 / Decimal.new 3) . should_fail_with Arithmetic_Error
|
||||
(Decimal.from_integer 1 / Decimal.from_integer 3) . should_fail_with Arithmetic_Error
|
||||
|
||||
group_builder.specify "should negate values correctly" <|
|
||||
Decimal.new 5 . negate . should_equal -5
|
||||
Decimal.new -5 . negate . should_equal 5
|
||||
Decimal.new 5.3 . negate . should_equal -5.3
|
||||
Decimal.new -5.3 . negate . should_equal 5.3
|
||||
Decimal.new 0 . negate . should_equal 0
|
||||
Decimal.new -0 . negate . should_equal 0
|
||||
d = Decimal.new 5
|
||||
Decimal.from_integer 5 . negate . should_equal -5
|
||||
Decimal.from_integer -5 . negate . should_equal 5
|
||||
Decimal.from_float 5.3 . negate . should_equal -5.3
|
||||
Decimal.from_float -5.3 . negate . should_equal 5.3
|
||||
Decimal.from_integer 0 . negate . should_equal 0
|
||||
Decimal.from_integer -0 . negate . should_equal 0
|
||||
d = Decimal.from_integer 5
|
||||
nd = -d
|
||||
nd . should_equal -5
|
||||
d2 = Decimal.new -5
|
||||
d2 = Decimal.from_integer -5
|
||||
nd2 = -d2
|
||||
nd2 . should_equal 5
|
||||
|
||||
group_builder.specify "Mixed Decimal/Integer arithmetic should not attach warnings" <|
|
||||
Problems.assume_no_problems (Decimal.new 1 + 2)
|
||||
Problems.assume_no_problems (1 + Decimal.new 2)
|
||||
|
||||
suite_builder.group "(Decimal_Spec) conversions" group_builder->
|
||||
group_builder.specify "should convert correctly to and from Integer" <|
|
||||
a = Decimal.new "12000"
|
||||
@ -600,7 +609,7 @@ add_specs suite_builder =
|
||||
(-huge_b).to_float . should_equal -huge_float
|
||||
(-huge_c).to_float . should_equal -huge_float
|
||||
|
||||
group_builder.specify "attaches a warning to the result of .to_float when it is out the representable range" <|
|
||||
group_builder.specify "attaches a warning to the result of .to_float when it is outside the representable range" <|
|
||||
huge_a = Decimal.new "3.4E320"
|
||||
f = huge_a.to_float
|
||||
f.should_equal Number.positive_infinity
|
||||
@ -611,14 +620,42 @@ add_specs suite_builder =
|
||||
Problems.expect_only_warning (Out_Of_Range.Error -huge_a "Outside representable Float range (approximately (-1.8E308, 1.8E308))") f2
|
||||
Warning.get_all f2 . at 0 . value . to_display_text . should_equal "Value out of range: -3.4E+320: Outside representable Float range (approximately (-1.8E308, 1.8E308))"
|
||||
|
||||
group_builder.specify "attaches a warning to the result of .to_float when it results in a loss of precision" <|
|
||||
Problems.expect_only_warning Loss_Of_Numeric_Precision (Decimal.new "0.1" . to_float)
|
||||
|
||||
group_builder.specify "should convert correctly to Float using implicit conversions" <|
|
||||
f = Float.from (Decimal.new "56.34")
|
||||
f.should_be_a Float
|
||||
f.should_equal 56.34
|
||||
|
||||
group_builder.specify "Explicit conversions to_decimal do not attach a warning" <|
|
||||
Problems.assume_no_problems (dec 0.1)
|
||||
Problems.assume_no_problems (0.1 . to_decimal)
|
||||
Problems.assume_no_problems (0.1 . to_decimal)
|
||||
Problems.assume_no_problems (Decimal.from_float 0.1)
|
||||
|
||||
group_builder.specify "Implicit conversions to_decimal attach a warning" <|
|
||||
Problems.expect_only_warning Loss_Of_Numeric_Precision (Decimal.new 0.1)
|
||||
Problems.expect_only_warning Loss_Of_Numeric_Precision (Decimal.from 0.1)
|
||||
Problems.expect_only_warning Loss_Of_Numeric_Precision (Decimal.from_float 0.1 explicit=False)
|
||||
Problems.expect_only_warning Loss_Of_Numeric_Precision (id_decimal 0.1)
|
||||
|
||||
group_builder.specify "`dec` never attaches a warning" <|
|
||||
Problems.assume_no_problems (dec "0.1")
|
||||
Problems.assume_no_problems (dec 10)
|
||||
Problems.assume_no_problems (dec 0.1)
|
||||
|
||||
group_builder.specify "Explicit conversions to Float do not attach a warning" <|
|
||||
Problems.assume_no_problems (Decimal.new "0.1" . to_float)
|
||||
|
||||
group_builder.specify "Arithmetic with decimal and explicitly converted float should succeed" <|
|
||||
((Decimal.new 1) + (dec 0.1)) . should_equal 1.1
|
||||
((Decimal.new 1) + (0.1 . to_decimal)) . should_equal 1.1
|
||||
((Decimal.new 1) + (0.1 . to_decimal)) . should_equal 1.1
|
||||
((Decimal.new 1) + (Decimal.from_float 0.1)) . should_equal 1.1
|
||||
|
||||
group_builder.specify "Arithmetic with decimal and implicitly converted float should fail" <|
|
||||
((Decimal.new 1) + (Decimal.from 0.1)) . should_fail_with Illegal_Argument
|
||||
((Decimal.new 1) + (id_decimal 0.1)) . should_fail_with Illegal_Argument
|
||||
((Decimal.new 1) + (Decimal.new 0.1)) . should_fail_with Illegal_Argument
|
||||
|
||||
group_builder.specify "should attach a Loss_Of_Numeric_Precision warning when converting decimal to float with .from" <|
|
||||
Problems.expect_only_warning Loss_Of_Numeric_Precision (Float.from (Decimal.new "56.34"))
|
||||
|
||||
@ -653,20 +690,18 @@ add_specs suite_builder =
|
||||
+ [["9223372036854775807", 10, 7], ["9223372036854775808", 10, 8], ["922337203685477580000000000008", 10, 8]]
|
||||
+ [["-9223372036854775808", 10, -8], ["-9223372036854775809", 10, -9], ["-922337203685477580000000000008", 10, -8]]
|
||||
cases.map c-> Test.with_clue ("["+c.to_text+"] ") <|
|
||||
base = c.at 0
|
||||
modulus = c.at 1
|
||||
residue = c.at 2
|
||||
epsilon = if c.length > 3 then c.at 3 else 0.0
|
||||
(Decimal.new base % modulus) . should_equal epsilon=epsilon residue
|
||||
(Decimal.new base % Decimal.new modulus) . should_equal residue
|
||||
base = dec (c.at 0)
|
||||
modulus = dec (c.at 1)
|
||||
residue = dec (c.at 2)
|
||||
(base % modulus) . should_equal residue
|
||||
|
||||
group_builder.specify "should define remainder (example)" <|
|
||||
(Decimal.new 10 . remainder 3) . should_equal 1
|
||||
(Decimal.new 10 % 3) . should_equal 1
|
||||
(Decimal.from_integer 10 . remainder 3) . should_equal 1
|
||||
(Decimal.from_integer 10 % 3) . should_equal 1
|
||||
|
||||
group_builder.specify "remainder 0 should throw" <|
|
||||
(Decimal.new 3 % 0) . should_fail_with Arithmetic_Error
|
||||
(Decimal.new 3 % Decimal.new 0) . should_fail_with Arithmetic_Error
|
||||
(Decimal.from_integer 3 % 0) . should_fail_with Arithmetic_Error
|
||||
(Decimal.from_integer 3 % Decimal.from_integer 0) . should_fail_with Arithmetic_Error
|
||||
|
||||
suite_builder.group "(Decimal_Spec) div" group_builder->
|
||||
group_builder.specify "should define div" <|
|
||||
@ -1000,9 +1035,6 @@ add_specs suite_builder =
|
||||
12 . to_decimal . should_be_a Decimal
|
||||
12.3 . to_decimal . should_be_a Decimal
|
||||
|
||||
group_builder.specify "Float.to_decimal should attach a warning" <|
|
||||
Problems.expect_warning Loss_Of_Numeric_Precision (12.3 . to_decimal)
|
||||
|
||||
group_builder.specify "examples" <|
|
||||
12 . to_decimal . should_equal (Decimal.new "12")
|
||||
12.3 . to_decimal . should_equal (Decimal.new "12.3")
|
||||
|
Loading…
Reference in New Issue
Block a user