Mixed Decimal/Float operations throw error or attach warning (#10725)

This commit is contained in:
GregoryTravis 2024-08-14 12:45:28 -04:00 committed by GitHub
parent 4d2cc04e17
commit e836373d9b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 440 additions and 173 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View 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

View File

@ -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

View File

@ -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")