mirror of
https://github.com/enso-org/enso.git
synced 2024-09-11 13:15:52 +03:00
Decimal
type: constuctors, comparisons, and arithmetic (#9272)
This commit is contained in:
parent
4332eab45a
commit
9a9eff1aa6
@ -633,6 +633,8 @@
|
||||
- [Added Google_Analytics.Read][9239]
|
||||
- [Added `Table.from_union` to allow expanding a vector of tables in one
|
||||
step][9343]
|
||||
- [Implemented constructors, comparisons, and arithmetic for a `Decimal`
|
||||
type.][9272]
|
||||
|
||||
[debug-shortcuts]:
|
||||
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
|
||||
@ -911,14 +913,15 @@
|
||||
[9215]: https://github.com/enso-org/enso/pull/9215
|
||||
[9225]: https://github.com/enso-org/enso/pull/9225
|
||||
[9233]: https://github.com/enso-org/enso/pull/9233
|
||||
[9239]: https://github.com/enso-org/enso/pull/9239
|
||||
[9249]: https://github.com/enso-org/enso/pull/9249
|
||||
[9269]: https://github.com/enso-org/enso/pull/9269
|
||||
[9272]: https://github.com/enso-org/enso/pull/9272
|
||||
[9299]: https://github.com/enso-org/enso/pull/9299
|
||||
[9330]: https://github.com/enso-org/enso/pull/9330
|
||||
[9334]: https://github.com/enso-org/enso/pull/9334
|
||||
[9346]: https://github.com/enso-org/enso/pull/9346
|
||||
[9382]: https://github.com/enso-org/enso/pull/9382
|
||||
[9239]: https://github.com/enso-org/enso/pull/9239
|
||||
[9343]: https://github.com/enso-org/enso/pull/9343
|
||||
|
||||
#### Enso Compiler
|
||||
|
584
distribution/lib/Standard/Base/0.0.0-dev/src/Data/Decimal.enso
Normal file
584
distribution/lib/Standard/Base/0.0.0-dev/src/Data/Decimal.enso
Normal file
@ -0,0 +1,584 @@
|
||||
import project.Any.Any
|
||||
import project.Data.Numeric.Internal.Decimal_Internal
|
||||
import project.Data.Numeric.Math_Context.Math_Context
|
||||
import project.Data.Numeric.Rounding_Mode.Rounding_Mode
|
||||
import project.Data.Text.Text
|
||||
import project.Error.Error
|
||||
import project.Errors.Common.Arithmetic_Error
|
||||
import project.Errors.Illegal_Argument.Illegal_Argument
|
||||
import project.Nothing.Nothing
|
||||
import project.Panic.Panic
|
||||
import project.Warning.Warning
|
||||
from project.Data.Boolean import Boolean, False, True
|
||||
from project.Data.Numbers import Integer, Float, Number, Number_Parse_Error
|
||||
from project.Data.Numeric.Internal.Decimal_Internal import Decimal_Comparator
|
||||
from project.Data.Ordering import Comparable, Ordering
|
||||
from project.Errors.Common import Loss_Of_Numeric_Precision
|
||||
|
||||
polyglot java import java.lang.ArithmeticException
|
||||
polyglot java import java.lang.NumberFormatException
|
||||
polyglot java import java.math.BigDecimal
|
||||
polyglot java import org.enso.base.numeric.ConversionResult
|
||||
polyglot java import org.enso.base.numeric.Decimal_Utils
|
||||
|
||||
## Decimal is the type of decimal (base-10-scaled) numbers in Enso. An Enso
|
||||
`Decimal` is a wrapper around a Java `BigDecimal`.
|
||||
|
||||
`Decimal` values have more overhead than standard `Float` values, but they
|
||||
have the advantage of being able to precisely specify values such as `0.1`,
|
||||
which is important for certain kinds of calculations, such as currency.
|
||||
|
||||
A `Decimal` value is represented internally by a Java `BigInteger` "unscaled
|
||||
value" and a "scale value". The numerical value of the `Decimal` is
|
||||
`(unscaledValue * 10^(-scale))`. Scale values are maintained automatically by
|
||||
the constructors and numerical operations.
|
||||
|
||||
Scale values can allow distinctions between values that would be identical as
|
||||
`Float`s. For example, the following values have different internal
|
||||
representations:
|
||||
|
||||
a = Decimal.new "2.0"
|
||||
b = Decimal.new "2.00"
|
||||
a == b
|
||||
# => True
|
||||
|
||||
These two values have different internal representations, but they are still
|
||||
considered the same value by `==`.
|
||||
|
||||
All of the constructors, and many of the operations, can take an additional
|
||||
`Math_Context` value, which can specify two things:
|
||||
|
||||
- precision: the number of decimal digits to use to represent a value or
|
||||
result; results are rounded to this precision.
|
||||
- rounding_mode: the method to use for rounding. See `Rounding_Mode` for
|
||||
details about each rounding method.
|
||||
|
||||
When a `Math_Context` value is used in an operation, and causes a loss of
|
||||
precision, a `Loss_Of_Numeric_Precision` warning is attached to the result.
|
||||
|
||||
If no `Math_Context` value is supplied, the default is to make all operations
|
||||
exact. A `Math_Context` with a precision of `0` has the same effect.
|
||||
|
||||
A `Decimal` can represent any `Float` precisely. However, some `Float`
|
||||
literals, such as `0.1`, cannot be represented exactly to infinite
|
||||
precision by a `Float`. For this reason, constructing a `Decimal` from
|
||||
a `Float` always attaches a `Loss_Of_Numeric_Precision` warning to the
|
||||
result. To avoid this problem, it is recommended to create `Decimal`s from
|
||||
fractional values by passing the value in as a `Text`, where possible and
|
||||
convenient.
|
||||
|
||||
The `Decimal` class provides arithmetic operations `add`, `subtract`,
|
||||
`multiply`, and `divide`, which can take a `Math_Context` argument. You can
|
||||
also use the usual operators `+`, `-`, `*`, `/`, which are the same as the
|
||||
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
|
||||
`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
|
||||
an explicit precision using a `Math_Context`.
|
||||
type Decimal
|
||||
Value (big_decimal : BigDecimal)
|
||||
|
||||
## Construct a `Decimal` from a string or integer.
|
||||
|
||||
Arguments:
|
||||
- x: The `Text`, `Integer`, or `Float` to construct a `Decimal` from.
|
||||
- mc: The `Math_Context` to use to specify precision and `Rounding_Mode`.
|
||||
If a `Math_Context` is used, there is a possibility of a loss of
|
||||
precision.
|
||||
|
||||
? Number Format
|
||||
|
||||
The textual format for a Decimal is defined at
|
||||
https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html#BigDecimal-java.lang.String-.
|
||||
|
||||
! Error Conditions
|
||||
|
||||
- If the `Text` argument is incorrectly formatted, a `Number_Parse_Error`
|
||||
is thrown.
|
||||
- If the construction of the Decimal results in a loss of precision, a
|
||||
`Loss_Of_Numeric_Precision` warning is attached. This can only happen
|
||||
if a `Math_Context` value is explicitly passed.
|
||||
|
||||
^ Example
|
||||
Create a `Decimal` from a string.
|
||||
|
||||
c = Decimal.new "12.345"
|
||||
new : Text | Integer | Float -> Math_Context -> Decimal ! Arithmetic_Error | Number_Parse_Error
|
||||
new (x : Text | Integer | Float) (mc : Math_Context | Nothing = Nothing) -> Decimal | Number_Parse_Error =
|
||||
handle_java_exception <|
|
||||
case x of
|
||||
_ : Text -> Decimal.parse x mc
|
||||
_ : Integer -> Decimal.from_integer x mc
|
||||
_ : Float -> Decimal.from_float x mc
|
||||
|
||||
## GROUP conversions
|
||||
Construct a `Decimal` from a `Text`.
|
||||
|
||||
Arguments:
|
||||
- s: The `Text` to construct a `Decimal` from.
|
||||
- mc: The `Math_Context` to use to specify precision and `Rounding_Mode`.
|
||||
If a `Math_Context` is used, there is a possibility of a loss of
|
||||
precision.
|
||||
|
||||
? Number Format
|
||||
|
||||
The textual format for a Decimal is defined at
|
||||
https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html#BigDecimal-java.lang.String-.
|
||||
|
||||
! Error Conditions
|
||||
|
||||
- If `s` is incorrectly formatted, a `Number_Parse_Error` is thrown.
|
||||
- If the construction of the Decimal results in a loss of precision, a
|
||||
`Loss_Of_Numeric_Precision` warning is attached. This can only happen
|
||||
if a `Math_Context` value is explicitly passed.
|
||||
|
||||
^ Example
|
||||
Create a `Decimal` from a string.
|
||||
|
||||
d = Decimal.parse "12.345"
|
||||
parse : Text -> Math_Context -> Decimal ! Arithmetic_Error | Number_Parse_Error
|
||||
parse (s : Text) (mc : Math_Context | Nothing = Nothing) -> Decimal | Number_Parse_Error =
|
||||
handle_java_exception <| handle_number_format_exception <|
|
||||
case mc of
|
||||
_ : Math_Context -> Decimal.Value <| handle_precision_loss s <| Decimal_Utils.fromString s mc.math_context
|
||||
_ : Nothing -> Decimal.Value (Decimal_Utils.fromString s)
|
||||
|
||||
## GROUP conversions
|
||||
Construct a `Decimal` from an `Integer`.
|
||||
|
||||
Arguments:
|
||||
- i: The `Integer` to construct a `Decimal` from.
|
||||
- mc: The `Math_Context` to use to specify precision and `Rounding_Mode`.
|
||||
If a `Math_Context` is used, there is a possibility of a loss of
|
||||
precision.
|
||||
|
||||
! Error Conditions
|
||||
|
||||
- If the construction of the Decimal results in a loss of precision, a
|
||||
`Loss_Of_Numeric_Precision` warning is attached. This can only happen
|
||||
if a `Math_Context` value is explicitly passed.
|
||||
|
||||
^ Example
|
||||
Create a `Decimal` from an integer.
|
||||
|
||||
d = Decimal.from_integer 12
|
||||
from_integer : Integer -> Math_Context -> Decimal ! Arithmetic_Error
|
||||
from_integer (i : Integer) (mc : Math_Context | Nothing = Nothing) -> Decimal ! Arithmetic_Error =
|
||||
handle_java_exception <|
|
||||
case mc of
|
||||
_ : Math_Context -> Decimal.Value <| handle_precision_loss i <| Decimal_Utils.fromInteger i mc.math_context
|
||||
_ : Nothing -> Decimal.Value (Decimal_Utils.fromInteger i)
|
||||
|
||||
## GROUP conversions
|
||||
Construct a `Decimal` from a `Float`.
|
||||
|
||||
Arguments:
|
||||
- f: The `Float` to construct a `Decimal` from.
|
||||
- mc: The `Math_Context` to use to specify precision and `Rounding_Mode`.
|
||||
If a `Math_Context` is used, there is a possibility of a loss of
|
||||
precision.
|
||||
|
||||
? Precision Loss
|
||||
|
||||
A `Decimal` can represent any `Float` precisely. However, some `Float`
|
||||
literals, such as `0.1`, cannot be represented exactly to infinite
|
||||
precision by a `Float`. For this reason, constructing a `Decimal` from
|
||||
a `Float` always attaches a `Loss_Of_Numeric_Precision` warning to the
|
||||
result.
|
||||
|
||||
! Error Conditions
|
||||
|
||||
- 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.
|
||||
|
||||
^ Example
|
||||
Create a `Decimal` from a float.
|
||||
|
||||
d = Decimal.from_integer 12.345
|
||||
from_float : Float -> Math_Context -> Decimal ! Arithmetic_Error ! Illegal_Argument
|
||||
from_float (f : Float) (mc : Math_Context | Nothing = Nothing) -> 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)
|
||||
|
||||
## ALIAS greater than
|
||||
GROUP Operators
|
||||
ICON operators
|
||||
Checks if this is greater than that.
|
||||
|
||||
Arguments:
|
||||
- that: The number to compare this against.
|
||||
|
||||
> Example
|
||||
Checking if 10 is greater than 7.
|
||||
|
||||
Decimal.new 10 > 7
|
||||
# => True
|
||||
> : Decimal -> Boolean
|
||||
> self (that : Decimal) -> Boolean = Decimal_Comparator.compare self that == Ordering.Greater
|
||||
|
||||
## ALIAS greater than or equal
|
||||
GROUP Operators
|
||||
ICON operators
|
||||
Checks if this is greater than or equal to that.
|
||||
|
||||
Arguments:
|
||||
- that: The number to compare this against.
|
||||
|
||||
> Example
|
||||
Checking if 10 is greater than or equal to 7.
|
||||
|
||||
Decimal.new 10 >= 7
|
||||
# => True
|
||||
>= : Decimal -> Boolean
|
||||
>= self (that : Decimal) -> Boolean =
|
||||
ordering = Decimal_Comparator.compare self that
|
||||
ordering == Ordering.Greater || ordering == Ordering.Equal
|
||||
|
||||
## ALIAS less than
|
||||
GROUP Operators
|
||||
ICON operators
|
||||
Checks if this is less than that.
|
||||
|
||||
Arguments:
|
||||
- that: The number to compare this against.
|
||||
|
||||
> Example
|
||||
Checking if 10 is less than 7.
|
||||
|
||||
Decimal.new 10 < 7
|
||||
# => False
|
||||
< : Decimal -> Boolean
|
||||
< self (that : Decimal) -> Boolean = Decimal_Comparator.compare self that == Ordering.Less
|
||||
|
||||
## ALIAS less than or equal
|
||||
GROUP Operators
|
||||
ICON operators
|
||||
Checks if this is less than or equal to that.
|
||||
|
||||
Arguments:
|
||||
- that: The number to compare this against.
|
||||
|
||||
> Example
|
||||
Checking if 10 is less than or equal to 7.
|
||||
|
||||
Decimal.new 10 <= 7
|
||||
# => False
|
||||
<= : Decimal -> Boolean
|
||||
<= self (that : Decimal) -> Boolean =
|
||||
ordering = Decimal_Comparator.compare self that
|
||||
ordering == Ordering.Less || ordering == Ordering.Equal
|
||||
|
||||
## ALIAS plus
|
||||
GROUP Operators
|
||||
ICON math
|
||||
Adds a `Decimal` to another `Decimal` or other kind of number. A
|
||||
`Math_Context` value can be specified to set the precision and
|
||||
`Rounding_Mode`.
|
||||
|
||||
Arguments:
|
||||
- that: The number to add to this.
|
||||
- math_context: Used to optionally specify precision and `Rounding_Mode`.
|
||||
|
||||
Addition in Enso will undergo automatic conversions such that you need
|
||||
not convert other numeric types to `Decimal` manually.
|
||||
|
||||
> Example
|
||||
Adding 10.22 and 20.33.
|
||||
|
||||
a = Decimal.new "10.22"
|
||||
b = Decimal.new "20.33"
|
||||
c = a.add b
|
||||
# => Decimal.new 30.55
|
||||
|
||||
> Example
|
||||
Adding 10.22 and 20.33, rounding to 3 digits of precision (1 decimal
|
||||
place).
|
||||
|
||||
a = Decimal.new "10.22"
|
||||
b = Decimal.new "20.33"
|
||||
a.add b (Math_Context.new 3)
|
||||
# => Decimal.new 30.5
|
||||
add : Decimal -> Decimal -> Math_Context | Nothing -> Decimal ! Arithmetic_Error
|
||||
add self (that : Decimal) (math_context : Math_Context | Nothing = Nothing) -> Decimal ! Arithmetic_Error =
|
||||
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)
|
||||
|
||||
## ALIAS plus
|
||||
GROUP Operators
|
||||
ICON math
|
||||
Adds a `Decimal` to another `Decimal` or other kind of number.
|
||||
|
||||
Arguments:
|
||||
- that: The number to add to this.
|
||||
|
||||
Addition in Enso will undergo automatic conversions such that you need
|
||||
not convert other numeric types to `Decimal` manually.
|
||||
|
||||
> Example
|
||||
Adding 10.22 and 20.33.
|
||||
|
||||
a = Decimal.new "10.22"
|
||||
b = Decimal.new "20.33"
|
||||
c = a + b
|
||||
# => Decimal.new 30.55
|
||||
+ : Decimal -> Decimal -> Decimal
|
||||
+ self (that : Decimal) = self.add that
|
||||
|
||||
## ALIAS minus
|
||||
GROUP Operators
|
||||
ICON math
|
||||
Subtract a `Decimal` or other kind of number from another `Decimal`, or
|
||||
subtract a `Decimal` from another kind of number. A `Math_Context` value
|
||||
can be specified to set the precision and `Rounding_Mode`.
|
||||
|
||||
Arguments:
|
||||
- that: The number to subtract from this.
|
||||
- math_context: Used to optionally specify precision and `Rounding_Mode`.
|
||||
|
||||
Subtraction in Enso will undergo automatic conversions such that you need
|
||||
not convert other numeric types to `Decimal` manually.
|
||||
|
||||
> Example
|
||||
Subtracting 10.22 from 20.33.
|
||||
|
||||
a = Decimal.new "20.33"
|
||||
b = Decimal.new "10.22"
|
||||
c = a.subtract b
|
||||
# => Decimal.new 10.11
|
||||
|
||||
> Example
|
||||
Subtracting 10.22 from 20.33, rounding to 3 digits of precision (1
|
||||
decimal place).
|
||||
|
||||
a = Decimal.new "20.33"
|
||||
b = Decimal.new "10.22"
|
||||
c = a.subtract b (Math_Context.new 3)
|
||||
# => Decimal.new 10.1
|
||||
subtract : Decimal -> Decimal -> Math_Context | Nothing -> Decimal ! Arithmetic_Error
|
||||
subtract self (that : Decimal) (math_context : Math_Context | Nothing = Nothing) -> Decimal ! Arithmetic_Error =
|
||||
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)
|
||||
|
||||
## ALIAS minus
|
||||
GROUP Operators
|
||||
ICON math
|
||||
Subtract a `Decimal` or other kind of number from another `Decimal`, or
|
||||
subtract a `Decimal` from another kind of number.
|
||||
|
||||
Arguments:
|
||||
- that: The number to subtract from this.
|
||||
|
||||
Subtraction in Enso will undergo automatic conversions such that you need
|
||||
not convert other numeric types to `Decimal` manually.
|
||||
|
||||
> Example
|
||||
Subtracting 10.22 from 20.33.
|
||||
|
||||
a = Decimal.new "20.33"
|
||||
b = Decimal.new "10.22"
|
||||
c = a - b
|
||||
# => Decimal.new 10.11
|
||||
- : Decimal -> Decimal -> Decimal
|
||||
- self (that : Decimal) = self.subtract that
|
||||
|
||||
## ALIAS times
|
||||
GROUP Operators
|
||||
ICON math
|
||||
Multiplies a `Decimal` by another `Decimal` or other kind of number. A
|
||||
`Math_Context` value can be specified to set the precision and
|
||||
`Rounding_Mode`.
|
||||
|
||||
Arguments:
|
||||
- that: The number to multiply by this.
|
||||
- math_context: Used to optionally specify precision and `Rounding_Mode`.
|
||||
|
||||
Multiplication in Enso will undergo automatic conversions such that you need
|
||||
not convert other numeric types to `Decimal` manually.
|
||||
|
||||
> Example
|
||||
Multiplying 10.22 and 20.33.
|
||||
|
||||
a = Decimal.new "10.22"
|
||||
b = Decimal.new "20.33"
|
||||
c = a.multiply b
|
||||
# => Decimal.new 207.7726
|
||||
|
||||
> Example
|
||||
Multiplying 10.22 and 20.33, rounding to 4 digits of precision (1
|
||||
decimal place).
|
||||
|
||||
a = Decimal.new "10.22"
|
||||
b = Decimal.new "20.33"
|
||||
c = a.multiply b (Math_Context.new 4)
|
||||
# => Decimal.new 207.8
|
||||
multiply : Decimal -> Decimal -> Math_Context | Nothing -> Decimal ! Arithmetic_Error
|
||||
multiply self (that : Decimal) (math_context : Math_Context | Nothing = Nothing) -> Decimal ! Arithmetic_Error =
|
||||
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)
|
||||
|
||||
## ALIAS times
|
||||
GROUP Operators
|
||||
ICON math
|
||||
Multiplies a `Decimal` by another `Decimal` or other kind of number.
|
||||
|
||||
Arguments:
|
||||
- that: The number to multiply by this.
|
||||
|
||||
Multiplication in Enso will undergo automatic conversions such that you need
|
||||
not convert other numeric types to `Decimal` manually.
|
||||
|
||||
> Example
|
||||
Multiplying 10.22 and 20.33.
|
||||
|
||||
a = Decimal.new "10.22"
|
||||
b = Decimal.new "20.33"
|
||||
c = a * b
|
||||
# => Decimal.new 207.7726
|
||||
* : Decimal -> Decimal -> Decimal
|
||||
* self (that : Decimal) = self.multiply that
|
||||
|
||||
## GROUP Operators
|
||||
ICON math
|
||||
Divide a `Decimal` by another `Decimal` or other kind of number, or
|
||||
divide another kind of number by a `Decimal`. A `Math_Context` value can
|
||||
be specified to set the precision and `Rounding_Mode`.
|
||||
|
||||
Arguments:
|
||||
- that: The number to divide by this.
|
||||
- math_context: Used to optionally specify precision and `Rounding_Mode`.
|
||||
|
||||
Division in Enso will undergo automatic conversions such that you need
|
||||
not convert other numeric types to `Decimal` manually.
|
||||
|
||||
! Error Conditions
|
||||
|
||||
- If the precision specified in `math_context` is 0, or if
|
||||
`match_context` is unspecified, and the quotient has a non-terminating
|
||||
decimal expansion, an `Arithmetic_Error` is thrown.
|
||||
|
||||
> Example
|
||||
Dividing 1065.9378 by 23.34.
|
||||
|
||||
a = Decimal.new "1065.9378"
|
||||
b = Decimal.new "23.34"
|
||||
c = a.divide b
|
||||
# => Decimal.new 45.67
|
||||
|
||||
> Example
|
||||
Dividing 1065.9378 by 23.34, rounding to 3 digits of precision (1
|
||||
decimal place).
|
||||
|
||||
a = Decimal.new "1065.9378"
|
||||
b = Decimal.new "23.34"
|
||||
c = a.divide b (Math_Context.new 3)
|
||||
# => Decimal.new 45.7
|
||||
divide : Decimal -> Decimal -> Math_Context | Nothing -> Decimal | Arithmetic_Error
|
||||
divide self (that : Decimal) (math_context : Math_Context | Nothing = Nothing) -> Decimal ! Arithmetic_Error =
|
||||
handle_java_exception <|
|
||||
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)
|
||||
|
||||
## GROUP Operators
|
||||
ICON math
|
||||
Divides a `Decimal` by another `Decimal` or other kind of number, or
|
||||
divides another kind of number by a `Decimal`.
|
||||
|
||||
Arguments:
|
||||
- that: The number to divide by this.
|
||||
|
||||
Division in Enso will undergo automatic conversions such that you need
|
||||
not convert other numeric types to `Decimal` manually.
|
||||
|
||||
> Example
|
||||
Dividing 1065.9378 by 23.34.
|
||||
|
||||
a = Decimal.new "1065.9378"
|
||||
b = Decimal.new "23.34"
|
||||
c = a / b
|
||||
# => Decimal.new 45.67
|
||||
/ : Decimal -> Decimal -> Decimal
|
||||
/ self (that : Decimal) = self.divide that
|
||||
|
||||
## GROUP Operators
|
||||
Compute the negation of this.
|
||||
|
||||
> Example
|
||||
Negate 5.1 to get -5.1.
|
||||
|
||||
5.1.negate
|
||||
# => Decimal.new -5.1
|
||||
negate : Decimal
|
||||
negate self = Decimal.Value self.big_decimal.negate
|
||||
|
||||
## PRIVATE
|
||||
precision : Integer
|
||||
precision self = self.big_decimal.precision
|
||||
|
||||
## PRIVATE
|
||||
scale : Integer
|
||||
scale self = self.big_decimal.scale
|
||||
|
||||
## PRIVATE
|
||||
TODO: This +0 is needed to allow internal representations to be compared.
|
||||
unscaled_value : Integer
|
||||
unscaled_value self = self.big_decimal.unscaledValue + 0
|
||||
|
||||
## PRIVATE
|
||||
internal_representation : [Integer]
|
||||
internal_representation self = [self.unscaled_value, self.precision, self.scale]
|
||||
|
||||
## PRIVATE
|
||||
to_text : Text
|
||||
to_text self = self.big_decimal.toString
|
||||
|
||||
## PRIVATE
|
||||
to_text_without_scientific_notation : Text
|
||||
to_text_without_scientific_notation self = self.big_decimal.toPlainString
|
||||
|
||||
## PRIVATE
|
||||
handle_number_format_exception ~action =
|
||||
Panic.catch NumberFormatException action caught_panic->
|
||||
Error.throw (Number_Parse_Error.Error caught_panic.payload.getMessage)
|
||||
|
||||
## PRIVATE
|
||||
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
|
||||
|
||||
## PRIVATE
|
||||
attach_loss_of_numeric_precision : Float -> Any -> Any
|
||||
attach_loss_of_numeric_precision f value =
|
||||
Warning.attach (Loss_Of_Numeric_Precision.Warning f f) value
|
||||
|
||||
handle_java_exception ~action =
|
||||
Panic.catch ArithmeticException action caught_panic->
|
||||
Error.throw (Arithmetic_Error.Error caught_panic.payload.getMessage)
|
||||
|
||||
## PRIVATE
|
||||
Comparable.from (_ : Decimal) = Decimal_Comparator
|
||||
|
||||
## PRIVATE
|
||||
Comparable.from (_ : Number) = Decimal_Comparator
|
||||
|
||||
## PRIVATE
|
||||
Decimal.from (that : Text) = Decimal.parse that
|
||||
|
||||
## PRIVATE
|
||||
Decimal.from (that : Number) = Decimal.new that
|
@ -0,0 +1,14 @@
|
||||
private
|
||||
|
||||
import project.Data.Decimal.Decimal
|
||||
import project.Data.Numbers.Number
|
||||
import project.Data.Text.Text
|
||||
from project.Data.Ordering import Comparable, Ordering
|
||||
|
||||
polyglot java import org.enso.base.numeric.Decimal_Utils
|
||||
|
||||
## PRIVATE
|
||||
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
|
@ -0,0 +1,13 @@
|
||||
private
|
||||
|
||||
from project.Data.Numbers import Float
|
||||
|
||||
polyglot java import java.lang.Double
|
||||
|
||||
## PRIVATE
|
||||
Maximum representable positive value, from the underlying Java double type.
|
||||
Float.max_value = Double.MAX_VALUE
|
||||
|
||||
## PRIVATE
|
||||
Minimum representable positive value, from the underlying Java double type.
|
||||
Float.min_value = Double.MIN_VALUE
|
@ -0,0 +1,47 @@
|
||||
import project.Data.Numeric.Rounding_Mode.Rounding_Mode
|
||||
from project.Data.Numbers import Integer
|
||||
|
||||
polyglot java import java.math.MathContext
|
||||
|
||||
## Specifies infinite precision in a `Math_Context`.
|
||||
|
||||
> Example
|
||||
Specify infinite precision in constructing a Decimal.
|
||||
|
||||
d1 = Decimal.new "1"
|
||||
d3 = Decimal.new "3"
|
||||
d4 = Decimal.new "4"
|
||||
|
||||
d1.divide d3 (Math_Context.new 12)
|
||||
# => 0.333333333333
|
||||
|
||||
d1.divide d3 (Math_Context.new Unlimited)
|
||||
# => Arithmetic_Error.Error 'Non-terminating decimal expansion; no exact representable decimal result.'
|
||||
|
||||
d1.divide d4 (Math_Context.new Unlimited)
|
||||
# => 0.25
|
||||
type Unlimited
|
||||
|
||||
## A wrapper around the Java
|
||||
[`MathContext`](https://docs.oracle.com/javase/8/docs/api/java/math/MathContext.html)
|
||||
class.
|
||||
|
||||
`Math_Context` is used to specify finite or infinite precision requirements
|
||||
to operations that construct `Decimal` values, or that compute them from
|
||||
other values. It can also specify the `Rounding_Mode` to use when rounding is
|
||||
used to meet precision requirements. See `Decimal` for more details.
|
||||
type Math_Context
|
||||
## PRIVATE
|
||||
Value (math_context:MathContext)
|
||||
|
||||
## Construct a `Math_Context` value.
|
||||
|
||||
Arguments:
|
||||
- precision: The non-negative int precision setting. A value of `0`
|
||||
specifies exact operations.
|
||||
- rounding_mode: The rounding mode to use.
|
||||
new : Integer -> Rounding_Mode -> Math_Context
|
||||
new (precision : Integer | Unlimited) (rounding_mode : Rounding_Mode = Rounding_Mode.half_up) =
|
||||
case precision of
|
||||
_ : Integer -> Math_Context.Value (MathContext.new precision rounding_mode.rounding_mode)
|
||||
_ : Unlimited -> Math_Context.Value (MathContext.new 0 rounding_mode.rounding_mode)
|
@ -0,0 +1,20 @@
|
||||
polyglot java import java.math.RoundingMode
|
||||
|
||||
## A wrapper around the Java
|
||||
[`RoundingMode`](https://docs.oracle.com/javase/8/docs/api/java/math/RoundingMode.html)
|
||||
class.
|
||||
|
||||
`Rounding_Mode` is used to specify the method to use for rounding a value in
|
||||
operations on `Decimal`s.` See `Decimal` for more details.
|
||||
type Rounding_Mode
|
||||
Value (rounding_mode : RoundingMode)
|
||||
|
||||
# Round towards positive infinity for positive numbers, and negative
|
||||
# infinity for negative numbers. (This is the default.)
|
||||
half_up : Rounding_Mode
|
||||
half_up = Rounding_Mode.Value RoundingMode.HALF_UP
|
||||
|
||||
## Rownd towards the nearest neighbor, with ties broken by rounding towards
|
||||
the nearest even neighbor.
|
||||
bankers : Rounding_Mode
|
||||
bankers = Rounding_Mode.Value RoundingMode.HALF_EVEN
|
@ -478,3 +478,19 @@ type Additional_Warnings
|
||||
to_display_text : Text
|
||||
to_display_text self =
|
||||
"There were "+self.count.to_text+" additional issues."
|
||||
|
||||
type Loss_Of_Numeric_Precision
|
||||
## PRIVATE
|
||||
Indicates that a numeric conversion of a value has lost precision.
|
||||
Warning original_value new_value
|
||||
|
||||
## PRIVATE
|
||||
Create a human-readable version of the error.
|
||||
to_display_text : Text
|
||||
to_display_text self =
|
||||
"Loss of precision in converting "+self.original_value.to_text+" to "+self.new_value.to_text
|
||||
|
||||
## PRIVATE
|
||||
to_text : Text
|
||||
to_text self =
|
||||
"(Loss_Of_Numeric_Precision.Warning (original_value = "+self.original_value.to_text+") (new_value = " + self.new_value.to_text+"))"
|
||||
|
@ -2,6 +2,7 @@ import project.Any.Any
|
||||
import project.Data
|
||||
import project.Data.Array.Array
|
||||
import project.Data.Boolean
|
||||
import project.Data.Decimal.Decimal
|
||||
import project.Data.Filter_Condition.Filter_Action
|
||||
import project.Data.Filter_Condition.Filter_Condition
|
||||
import project.Data.Index_Sub_Range.Index_Sub_Range
|
||||
@ -15,6 +16,9 @@ import project.Data.Locale.Locale
|
||||
import project.Data.Map.Map
|
||||
import project.Data.Maybe.Maybe
|
||||
import project.Data.Numbers
|
||||
import project.Data.Numeric.Math_Context.Math_Context
|
||||
import project.Data.Numeric.Math_Context.Unlimited
|
||||
import project.Data.Numeric.Rounding_Mode.Rounding_Mode
|
||||
import project.Data.Ordering.Comparable
|
||||
import project.Data.Ordering.Default_Comparator
|
||||
import project.Data.Ordering.Natural_Order
|
||||
@ -101,6 +105,7 @@ from project.System.File_Format.Plain_Text_Format import Plain_Text
|
||||
export project.Any.Any
|
||||
export project.Data
|
||||
export project.Data.Array.Array
|
||||
export project.Data.Decimal.Decimal
|
||||
export project.Data.Filter_Condition.Filter_Action
|
||||
export project.Data.Filter_Condition.Filter_Condition
|
||||
export project.Data.Index_Sub_Range.Index_Sub_Range
|
||||
@ -113,6 +118,9 @@ export project.Data.List.List
|
||||
export project.Data.Locale.Locale
|
||||
export project.Data.Map.Map
|
||||
export project.Data.Maybe.Maybe
|
||||
export project.Data.Numeric.Math_Context.Math_Context
|
||||
export project.Data.Numeric.Math_Context.Unlimited
|
||||
export project.Data.Numeric.Rounding_Mode.Rounding_Mode
|
||||
export project.Data.Ordering.Comparable
|
||||
export project.Data.Ordering.Default_Comparator
|
||||
export project.Data.Ordering.Natural_Order
|
||||
|
@ -541,7 +541,7 @@ class IrToTruffle(
|
||||
true
|
||||
)
|
||||
|
||||
val operators = ".!$%&*+-/<>?^~\\"
|
||||
val operators = ".!$%&*+-/<>?^~\\="
|
||||
def isOperator(n: Name): Boolean = {
|
||||
n.name
|
||||
.chars()
|
||||
|
@ -0,0 +1,6 @@
|
||||
package org.enso.base.numeric;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
// Wraps the result of a conversion to BigDecimal with a precision loss flag.
|
||||
public record ConversionResult(BigDecimal newValue, boolean hasPrecisionLoss) {}
|
@ -0,0 +1,94 @@
|
||||
package org.enso.base.numeric;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.math.MathContext;
|
||||
|
||||
/** Utils for the Enso Decmial type. */
|
||||
public class Decimal_Utils {
|
||||
private static final BigDecimal MIN_LONG_BIGDECIMAL = BigDecimal.valueOf(Long.MIN_VALUE);
|
||||
private static final BigDecimal MAX_LONG_BIGDECIMAL = BigDecimal.valueOf(Long.MAX_VALUE);
|
||||
|
||||
public static BigDecimal fromString(String s) {
|
||||
return new BigDecimal(s);
|
||||
}
|
||||
|
||||
public static ConversionResult fromString(String s, MathContext mc) {
|
||||
BigDecimal bd = new BigDecimal(s, mc);
|
||||
BigDecimal withoutMC = new BigDecimal(s);
|
||||
return new ConversionResult(bd, bd.compareTo(withoutMC) != 0);
|
||||
}
|
||||
|
||||
public static BigDecimal fromInteger(Object o) {
|
||||
if (o instanceof Long l) {
|
||||
// According to the BigInteger Javadocs, valueOf is preferred "because it
|
||||
// allows for reuse of frequently used BigDecimal values".
|
||||
return BigDecimal.valueOf(l);
|
||||
} else if (o instanceof BigInteger bi) {
|
||||
return new BigDecimal(bi);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Input must be Long or BigInteger");
|
||||
}
|
||||
}
|
||||
|
||||
public static ConversionResult fromInteger(Object o, MathContext mc) {
|
||||
switch (o) {
|
||||
case Long l -> {
|
||||
BigDecimal bd = new BigDecimal(l, mc);
|
||||
BigDecimal withoutMC = new BigDecimal(l);
|
||||
long backToLong = bd.longValue();
|
||||
return new ConversionResult(bd, bd.compareTo(withoutMC) != 0);
|
||||
}
|
||||
case BigInteger bi -> {
|
||||
BigDecimal bd = new BigDecimal(bi, mc);
|
||||
BigDecimal withoutMC = new BigDecimal(bi);
|
||||
BigInteger backToBigInteger = bd.toBigInteger();
|
||||
return new ConversionResult(bd, bd.compareTo(withoutMC) != 0);
|
||||
}
|
||||
case null, default -> {
|
||||
throw new IllegalArgumentException("Input must be Long or BigInteger");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static BigDecimal fromFloat(Double d) {
|
||||
// According to the BigInteger Javadocs, valueOf is preferred because "the
|
||||
// value returned is equal to that resulting from constructing a BigDecimal
|
||||
// from the result of using Double.toString(double)."
|
||||
return BigDecimal.valueOf(d);
|
||||
}
|
||||
|
||||
public static ConversionResult fromFloat(Double d, MathContext mc) {
|
||||
// We do not check for precision loss here because we always attach a
|
||||
// warning when converting from float.
|
||||
return new ConversionResult(new BigDecimal(d, mc), true);
|
||||
}
|
||||
|
||||
public static boolean fitsInLong(BigDecimal bigDecimal) {
|
||||
return bigDecimal.compareTo(MIN_LONG_BIGDECIMAL) >= 0
|
||||
&& bigDecimal.compareTo(MAX_LONG_BIGDECIMAL) <= 0;
|
||||
}
|
||||
|
||||
public static int hashCodeOf(BigDecimal bd) {
|
||||
boolean isFractional = bd.remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) != 0;
|
||||
boolean fitsInLong = fitsInLong(bd);
|
||||
if (isFractional || fitsInLong) {
|
||||
double d = bd.doubleValue();
|
||||
var fitsInDouble = Double.isFinite(d);
|
||||
if (fitsInDouble) {
|
||||
return Double.hashCode(d);
|
||||
} else {
|
||||
// Infinite values here just means finite values outside the double
|
||||
// range. In this path, the values must be fractional, and so cannot
|
||||
// coincide with a value of any other type (including BigInteger), so we
|
||||
// can hash it however we want.
|
||||
assert isFractional;
|
||||
return bd.hashCode();
|
||||
}
|
||||
} else {
|
||||
// Will not throw ArithmeticException since the value has a 0 fractional part.
|
||||
assert !isFractional;
|
||||
return bd.toBigIntegerExact().hashCode();
|
||||
}
|
||||
}
|
||||
}
|
498
test/Base_Tests/src/Data/Decimal_Spec.enso
Normal file
498
test/Base_Tests/src/Data/Decimal_Spec.enso
Normal file
@ -0,0 +1,498 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Data.Numeric.Internal.Numbers_Constants
|
||||
import Standard.Base.Errors.Common.Arithmetic_Error
|
||||
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
|
||||
|
||||
from Standard.Base.Data.Numbers import Number_Parse_Error
|
||||
from Standard.Base.Errors.Common import Loss_Of_Numeric_Precision
|
||||
|
||||
from Standard.Test import all
|
||||
|
||||
polyglot java import org.enso.base.numeric.Decimal_Utils
|
||||
|
||||
Decimal.should_have_rep self rep = self.internal_representation . should_equal rep
|
||||
|
||||
add_specs suite_builder =
|
||||
suite_builder.group "construction" group_builder->
|
||||
group_builder.specify "should be able to construct a Decimal from a string" <|
|
||||
Decimal.new "123.45" . should_have_rep [12345, 5, 2]
|
||||
Decimal.parse "123.45" . should_have_rep [12345, 5, 2]
|
||||
|
||||
group_builder.specify "should throw Number_Parse_Error on a badly-formatted string" <|
|
||||
Decimal.new "ee" . should_fail_with Number_Parse_Error
|
||||
Decimal.new "--123.3.3" . should_fail_with Number_Parse_Error
|
||||
|
||||
group_builder.specify "should be able to construct a Decimal from a string, with an explicit precision, using the default rounding" <|
|
||||
mc = Math_Context.new 4
|
||||
Decimal.new "123.45" mc . should_have_rep [1235, 4, 1]
|
||||
Decimal.new "123.45" mc . should_equal (Decimal.new "123.5")
|
||||
|
||||
group_builder.specify "should be able to construct a Decimal from a string, with an explicit precision, using a non-default rounding" <|
|
||||
mc = Math_Context.new 4 Rounding_Mode.bankers
|
||||
Decimal.new "123.45" mc . should_have_rep [1234, 4, 1]
|
||||
Decimal.new "123.45" mc . should_equal (Decimal.new "123.4")
|
||||
|
||||
group_builder.specify "should be able to construct a Decimal from a long string" <|
|
||||
Decimal.new "495782984723948723947239938732974241.2345" . should_have_rep [4957829847239487239472399387329742412345, 40, 4]
|
||||
Decimal.parse "495782984723948723947239938732974241.2345" . should_have_rep [4957829847239487239472399387329742412345, 40, 4]
|
||||
|
||||
group_builder.specify "should be able to construct a Decimal from a small integer" <|
|
||||
Decimal.new 1234500 . should_have_rep [1234500, 7, 0]
|
||||
|
||||
group_builder.specify "should be able to construct a Decimal from a small integer, with an explicit precision, using the default rounding" <|
|
||||
mc = Math_Context.new 4
|
||||
Decimal.new 1234500 mc . should_have_rep [1235, 4, -3]
|
||||
Decimal.new 1234500 mc . should_equal (Decimal.new 1235000)
|
||||
|
||||
group_builder.specify "should be able to construct a Decimal from a small integer, with an explicit precision, using the non-default rounding" <|
|
||||
mc = Math_Context.new 4 Rounding_Mode.bankers
|
||||
Decimal.new 1234500 mc . should_have_rep [1234, 4, -3]
|
||||
Decimal.new 1234500 mc . should_equal (Decimal.new 1234000)
|
||||
|
||||
group_builder.specify "should be able to construct a Decimal from a large integer" <|
|
||||
Decimal.new 495782984723948723947239938732974241234500 . should_have_rep [495782984723948723947239938732974241234500 , 42, 0]
|
||||
|
||||
group_builder.specify "should be able to construct a Decimal from a large integer, with an explicit precision, using the default rounding" <|
|
||||
mc = Math_Context.new 39
|
||||
Decimal.new 495782984723948723947239938732974241234500 mc . should_have_rep [495782984723948723947239938732974241235 , 39, -3]
|
||||
Decimal.new 495782984723948723947239938732974241234500 mc . should_equal (Decimal.new 495782984723948723947239938732974241235000 )
|
||||
|
||||
group_builder.specify "should be able to construct a Decimal from a large integer, with an explicit precision, using the non-default rounding" <|
|
||||
mc = Math_Context.new 39 Rounding_Mode.bankers
|
||||
Decimal.new 495782984723948723947239938732974241234500 mc . should_have_rep [495782984723948723947239938732974241234 , 39, -3]
|
||||
Decimal.new 495782984723948723947239938732974241234500 mc . should_equal (Decimal.new 495782984723948723947239938732974241234000 )
|
||||
|
||||
group_builder.specify "should be able to construct a Decimal from a float" <|
|
||||
d = Decimal.from_float 123.45
|
||||
d.should_have_rep [12345, 5, 2]
|
||||
|
||||
group_builder.specify "should be able to construct a Decimal from a float, with an explicit precision, using the default rounding" <|
|
||||
mc = Math_Context.new 5
|
||||
d = Decimal.from_float 123.125 mc
|
||||
d.should_have_rep [12313, 5, 2]
|
||||
d.should_equal (Decimal.new "123.13")
|
||||
|
||||
group_builder.specify "should be able to construct a Decimal from a float, with an explicit precision, using a non-default rounding" <|
|
||||
mc = Math_Context.new 5 Rounding_Mode.bankers
|
||||
d = Decimal.from_float 123.125 mc
|
||||
d.should_have_rep [12312, 5, 2]
|
||||
d.should_equal (Decimal.new "123.12")
|
||||
|
||||
group_builder.specify "should be able to explicity and implicitly specify infinite precision" <|
|
||||
Decimal.new "123.125" (Math_Context.new 5) . should_equal 123.13
|
||||
Decimal.new "123.125" (Math_Context.new Unlimited) . should_equal 123.125
|
||||
Decimal.new "123.125" (Math_Context.new 0) . should_equal 123.125
|
||||
|
||||
d1 = Decimal.new "1"
|
||||
d3 = Decimal.new "3"
|
||||
d4 = Decimal.new "4"
|
||||
|
||||
d1.divide d4 (Math_Context.new Unlimited) . should_equal 0.25
|
||||
d1.divide d3 (Math_Context.new 12) . internal_representation . should_equal [333333333333, 12, 12]
|
||||
d1.divide d3 . should_fail_with Arithmetic_Error
|
||||
d1.divide d3 (Math_Context.new Unlimited) . should_fail_with Arithmetic_Error
|
||||
|
||||
group_builder.specify "should report precision loss accurately" <|
|
||||
mc4 = Math_Context.new 4
|
||||
mc5 = Math_Context.new 5
|
||||
|
||||
Problems.not_expect_warning (Decimal.new "123.25")
|
||||
Problems.expect_warning Loss_Of_Numeric_Precision (Decimal.new "123.25" mc4)
|
||||
Problems.not_expect_warning Loss_Of_Numeric_Precision (Decimal.new "123.25" mc5)
|
||||
|
||||
Problems.expect_warning Loss_Of_Numeric_Precision (Decimal.new 123.25)
|
||||
Problems.expect_warning Loss_Of_Numeric_Precision (Decimal.new 123.25 mc4)
|
||||
Problems.expect_warning Loss_Of_Numeric_Precision (Decimal.new 123.25 mc5)
|
||||
|
||||
Problems.not_expect_warning (Decimal.new 12325)
|
||||
Problems.expect_warning Loss_Of_Numeric_Precision (Decimal.new 12325 mc4)
|
||||
Problems.not_expect_warning Loss_Of_Numeric_Precision (Decimal.new 12325 mc5)
|
||||
|
||||
group_builder.specify "should throw Illegal_Argument for NaN/Inf" <|
|
||||
Decimal.new Number.nan . should_fail_with Illegal_Argument
|
||||
Decimal.new Number.positive_infinity . should_fail_with Illegal_Argument
|
||||
Decimal.new Number.negative_infinity . should_fail_with Illegal_Argument
|
||||
|
||||
group_builder.specify "should be convertible via .from" <|
|
||||
Decimal.from "123.45" . should_equal (Decimal.new "123.45")
|
||||
Decimal.from "123.45" . should_equal (Decimal.parse "123.45")
|
||||
Decimal.from 123.45 . should_equal (Decimal.new 123.45)
|
||||
Decimal.from 123.45 . should_equal (Decimal.from_float 123.45)
|
||||
Decimal.from 12345 . should_equal (Decimal.new 12345)
|
||||
Decimal.from 12345 . should_equal (Decimal.from_integer 12345)
|
||||
|
||||
group_builder.specify "constructor should respect Math_Context " <|
|
||||
Decimal.new 12000 (Math_Context.new 0) . should_equal 12000
|
||||
Decimal.new 12000 (Math_Context.new 1) . should_equal 10000
|
||||
Decimal.new 12000 (Math_Context.new 2) . should_equal 12000
|
||||
Decimal.new 12000 (Math_Context.new 3) . should_equal 12000
|
||||
|
||||
suite_builder.group "comparison" group_builder->
|
||||
group_builder.specify "should compare correctly" <|
|
||||
nums = [["-45.23", "124.872"], [-45.23, 124.872], [-45, 124]]
|
||||
nums.map pr->
|
||||
a = Decimal.new (pr.at 0)
|
||||
b = Decimal.new (pr.at 1)
|
||||
|
||||
(a == a) . should_be_true
|
||||
(b == b) . should_be_true
|
||||
(a == b) . should_be_false
|
||||
(b == a) . should_be_false
|
||||
|
||||
(a != a) . should_be_false
|
||||
(b != b) . should_be_false
|
||||
(a != b) . should_be_true
|
||||
(b != a) . should_be_true
|
||||
|
||||
(a <= a) . should_be_true
|
||||
(b <= b) . should_be_true
|
||||
(a <= b) . should_be_true
|
||||
(b <= a) . should_be_false
|
||||
|
||||
(a >= a) . should_be_true
|
||||
(b >= b) . should_be_true
|
||||
(a >= b) . should_be_false
|
||||
(b >= a) . should_be_true
|
||||
|
||||
(a < b) . should_be_true
|
||||
(b > a) . should_be_true
|
||||
(a > b) . should_be_false
|
||||
(b < a) . should_be_false
|
||||
|
||||
(a <= b) . should_be_true
|
||||
(b >= a) . should_be_true
|
||||
(a >= b) . should_be_false
|
||||
(b <= a) . should_be_false
|
||||
|
||||
(a < a) . should_be_false
|
||||
(b < b) . should_be_false
|
||||
(a > a) . should_be_false
|
||||
(b > b) . should_be_false
|
||||
|
||||
a . should_equal a
|
||||
b . should_equal b
|
||||
a . should_not_equal b
|
||||
b . should_not_equal a
|
||||
|
||||
group_builder.specify "should compare correctly, even with different internal representations" <|
|
||||
a = Decimal.new "12000"
|
||||
b = Decimal.new "12000" (Math_Context.new 2)
|
||||
c = Decimal.new "12000" (Math_Context.new 3)
|
||||
[[a, b], [a, c], [b, c]].map pr->
|
||||
x0 = pr.at 0
|
||||
x1 = pr.at 1
|
||||
ir0 = x0.internal_representation
|
||||
ir1 = x1.internal_representation
|
||||
|
||||
ir0 . should_not_equal ir1
|
||||
|
||||
(x0 == x1) . should_be_true
|
||||
(x1 == x0) . should_be_true
|
||||
(x0 != x1) . should_be_false
|
||||
(x1 != x0) . should_be_false
|
||||
|
||||
group_builder.specify "should compare correctly to Integer and Float" <|
|
||||
ok_values = []
|
||||
+ [[0.1, 0.1]]
|
||||
+ [["0.1", 0.1]]
|
||||
+ [["0", 0]]
|
||||
+ [["1", 1]]
|
||||
+ [["-1", -1]]
|
||||
+ [["2", 2]]
|
||||
+ [["-2", -2]]
|
||||
+ [["0", 0.0]]
|
||||
+ [["1", 1.0]]
|
||||
+ [["-1", -1.0]]
|
||||
+ [["2", 2.0]]
|
||||
+ [["-2", -2.0]]
|
||||
+ [[0, 0]]
|
||||
+ [[1, 1]]
|
||||
+ [[-1, -1]]
|
||||
+ [[2, 2]]
|
||||
+ [[-2, -2]]
|
||||
+ [[0, 0.0]]
|
||||
+ [[1, 1.0]]
|
||||
+ [[-1, -1.0]]
|
||||
+ [[2, 2.0]]
|
||||
+ [[-2, -2.0]]
|
||||
+ [["12.34", 12.34]]
|
||||
+ [["-34.56", -34.56]]
|
||||
+ [["72775434512.34", 72775434512.34]]
|
||||
+ [["-347757349.56784374", -347757349.56784374]]
|
||||
+ [[12.34, 12.34]]
|
||||
+ [[-34.56, -34.56]]
|
||||
+ [[72775434512.34, 72775434512.34]]
|
||||
+ [[-347757349.56784374, -347757349.56784374]]
|
||||
+ [["2.0", 2.0]]
|
||||
+ [["2.00", 2.0]]
|
||||
+ [["2.000", 2.0]]
|
||||
+ [[9223372036854770000.0, 9223372036854770000.0]]
|
||||
+ [[9223372036854770000000.0, 9223372036854770000000.0]]
|
||||
+ [[92233720368547700000000000000000000000.0, 92233720368547700000000000000000000000.0]]
|
||||
+ [[9223372036854775805, 9223372036854775805]]
|
||||
+ [[9223372036854775806, 9223372036854775806]]
|
||||
+ [[9223372036854775807, 9223372036854775807]]
|
||||
+ [[9223372036854775808, 9223372036854775808]]
|
||||
+ [[9223372036854775809, 9223372036854775809]]
|
||||
+ [[9223372036854775999, 9223372036854775999]]
|
||||
+ [[9223372036854776000, 9223372036854776000]]
|
||||
+ [[9223372036854776001, 9223372036854776001]]
|
||||
+ [[9223372036854777000, 9223372036854777000]]
|
||||
+ [[-9223372036854775810, -9223372036854775810]]
|
||||
+ [[-9223372036854775809, -9223372036854775809]]
|
||||
+ [[-9223372036854775808, -9223372036854775808]]
|
||||
+ [[-9223372036854775807, -9223372036854775807]]
|
||||
+ [[-9223372036854775807, -9223372036854775807]]
|
||||
+ [[-9223372036854775806, -9223372036854775806]]
|
||||
+ [[3946372036854775806000, 3946372036854775806000]]
|
||||
+ [[3946372036854775807000, 3946372036854775807000]]
|
||||
+ [[3946372036854775808000, 3946372036854775808000]]
|
||||
+ [[-3946372036854775809000, -3946372036854775809000]]
|
||||
+ [[-3946372036854775808000, -3946372036854775808000]]
|
||||
+ [[-3946372036854775807000, -3946372036854775807000]]
|
||||
+ [[39463720368547758060000000, 39463720368547758060000000]]
|
||||
+ [[39463720368547758070000000, 39463720368547758070000000]]
|
||||
+ [[39463720368547758080000000, 39463720368547758080000000]]
|
||||
+ [[-39463720368547758090000000, -39463720368547758090000000]]
|
||||
+ [[-39463720368547758080000000, -39463720368547758080000000]]
|
||||
+ [[-39463720368547758070000000, -39463720368547758070000000]]
|
||||
+ [["9223372036854775805", 9223372036854775805]]
|
||||
+ [["9223372036854775806", 9223372036854775806]]
|
||||
+ [["9223372036854775807", 9223372036854775807]]
|
||||
+ [["9223372036854775808", 9223372036854775808]]
|
||||
+ [["9223372036854775809", 9223372036854775809]]
|
||||
+ [["9223372036854775999", 9223372036854775999]]
|
||||
+ [["9223372036854776000", 9223372036854776000]]
|
||||
+ [["9223372036854776001", 9223372036854776001]]
|
||||
+ [["9223372036854777000", 9223372036854777000]]
|
||||
+ [["-9223372036854775810", -9223372036854775810]]
|
||||
+ [["-9223372036854775809", -9223372036854775809]]
|
||||
+ [["-9223372036854775808", -9223372036854775808]]
|
||||
+ [["-9223372036854775807", -9223372036854775807]]
|
||||
+ [["-9223372036854775807", -9223372036854775807]]
|
||||
+ [["-9223372036854775806", -9223372036854775806]]
|
||||
+ [["3946372036854775806000", 3946372036854775806000]]
|
||||
+ [["3946372036854775807000", 3946372036854775807000]]
|
||||
+ [["3946372036854775808000", 3946372036854775808000]]
|
||||
+ [["-3946372036854775809000", -3946372036854775809000]]
|
||||
+ [["-3946372036854775808000", -3946372036854775808000]]
|
||||
+ [["-3946372036854775807000", -3946372036854775807000]]
|
||||
+ [["39463720368547758060000000", 39463720368547758060000000]]
|
||||
+ [["39463720368547758070000000", 39463720368547758070000000]]
|
||||
+ [["39463720368547758080000000", 39463720368547758080000000]]
|
||||
+ [["-39463720368547758090000000", -39463720368547758090000000]]
|
||||
+ [["-39463720368547758080000000", -39463720368547758080000000]]
|
||||
+ [["-39463720368547758070000000", -39463720368547758070000000]]
|
||||
+ [[Float.max_value, Float.max_value]]
|
||||
+ [[-Float.max_value, -Float.max_value]]
|
||||
+ [[Float.min_value, Float.min_value]]
|
||||
+ [[-Float.min_value, -Float.min_value]]
|
||||
+ [[Float.max_value-1.0, Float.max_value-1.0]]
|
||||
+ [[-Float.max_value+1.0, -Float.max_value+1.0]]
|
||||
|
||||
problematic_values = []
|
||||
+ [[9223372036854776000.0, 9223372036854776000.0]]
|
||||
+ [[-9223372036854776000.0, -9223372036854776000.0]]
|
||||
+ [["9223372036854776000.0", 9223372036854776000.0]]
|
||||
+ [["-9223372036854776000.0", -9223372036854776000.0]]
|
||||
|
||||
# TODO: Include problematic values pending https://github.com/enso-org/enso/issues/9296.
|
||||
values = ok_values # + problematic_values
|
||||
_ = [problematic_values]
|
||||
|
||||
values.map pr->
|
||||
v = pr.at 0
|
||||
d = Decimal.new v
|
||||
expected = pr.at 1
|
||||
|
||||
d . should_equal expected
|
||||
expected . should_equal d
|
||||
(d == expected) . should_be_true
|
||||
(expected == d) . should_be_true
|
||||
(d != expected) . should_be_false
|
||||
(expected != d) . should_be_false
|
||||
|
||||
if v.is_a Text . not then
|
||||
lesser = if v > 10000 then v/2 else if v < -10000 then v*2 else v-1
|
||||
greater = if v > 10000 then v*2 else if v < -10000 then v/2 else v+1
|
||||
(v > lesser) . should_be_true
|
||||
(v < greater) . should_be_true
|
||||
|
||||
(d <= v) . should_be_true
|
||||
(d >= v) . should_be_true
|
||||
(v <= d) . should_be_true
|
||||
(v >= d) . should_be_true
|
||||
|
||||
(d < d) . should_be_false
|
||||
(d > d) . should_be_false
|
||||
(d <= d) . should_be_true
|
||||
(d >= d) . should_be_true
|
||||
|
||||
if greater.is_infinite.not then
|
||||
(d < greater) . should_be_true
|
||||
(d <= greater) . should_be_true
|
||||
(greater > d) . should_be_true
|
||||
(greater >= d) . should_be_true
|
||||
(d > greater) . should_be_false
|
||||
(d >= greater) . should_be_false
|
||||
(greater < d) . should_be_false
|
||||
(greater <= d) . should_be_false
|
||||
|
||||
if lesser.is_infinite.not then
|
||||
(d > lesser) . should_be_true
|
||||
(d >= lesser) . should_be_true
|
||||
(lesser < d) . should_be_true
|
||||
(lesser <= d) . should_be_true
|
||||
(d < lesser) . should_be_false
|
||||
(d <= lesser) . should_be_false
|
||||
(lesser > d) . should_be_false
|
||||
(lesser >= d) . should_be_false
|
||||
|
||||
suite_builder.group "edge cases" group_builder->
|
||||
group_builder.specify "can support values outside the double range" <|
|
||||
d = Decimal.new Float.max_value
|
||||
(d == Float.max_value) . should_be_true
|
||||
((d * d) == Float.max_value) . should_be_false
|
||||
((-(d * d)) == -Float.max_value) . should_be_false
|
||||
Comparable.hash_builtin (d * d) . should_equal -1851772048
|
||||
Comparable.hash_builtin -(d * d) . should_equal 1851772048
|
||||
Comparable.hash_builtin ((d * d) + 0.1) . should_equal -1176480326
|
||||
Comparable.hash_builtin ((-(d * d)) + 0.1) . should_equal -534461530
|
||||
|
||||
suite_builder.group "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.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.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.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.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.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.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.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.new 213427523957 + Decimal.new 93849398884384) . should_equal (Decimal.new 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.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.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.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.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.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.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)
|
||||
|
||||
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.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.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.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.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.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.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.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)
|
||||
|
||||
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
|
||||
|
||||
(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
|
||||
|
||||
(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
|
||||
|
||||
(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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
nd = -d
|
||||
nd . should_equal -5
|
||||
d2 = Decimal.new -5
|
||||
nd2 = -d2
|
||||
nd2 . should_equal 5
|
||||
|
||||
main =
|
||||
suite = Test.build suite_builder->
|
||||
add_specs suite_builder
|
||||
suite.run_with_filter
|
@ -27,6 +27,12 @@ type Complex
|
||||
|
||||
< self (that:Complex) = Complex_Comparator.compare self that == Ordering.Less
|
||||
> self (that:Complex) = Complex_Comparator.compare self that == Ordering.Greater
|
||||
>= self (that:Complex) =
|
||||
ordering = Complex_Comparator.compare self that
|
||||
ordering == Ordering.Greater || ordering == Ordering.Equal
|
||||
<= self (that:Complex) =
|
||||
ordering = Complex_Comparator.compare self that
|
||||
ordering == Ordering.Less || ordering == Ordering.Equal
|
||||
|
||||
Complex.from (that:Number) = Complex.new that
|
||||
|
||||
@ -570,6 +576,16 @@ add_specs suite_builder =
|
||||
3.truncate . should_equal 3
|
||||
-3.truncate . should_equal -3
|
||||
|
||||
suite_builder.group "Hashing" group_builder->
|
||||
# TODO: Pending https://github.com/enso-org/enso/issues/9296
|
||||
group_builder.specify "Hash consistency for values near Double.MIN/MAX_VALUE" pending="hash consistency" <|
|
||||
ok_values = [9223372036854775000, -9223372036854775000]
|
||||
bad_values = [9223372036854776000, -9223372036854776000]
|
||||
values = ok_values + bad_values
|
||||
values.map i->
|
||||
f = i.to_float
|
||||
(Comparable.hash_builtin f) . should_equal (Comparable.hash_builtin i)
|
||||
|
||||
suite_builder.group "Number Conversions" group_builder->
|
||||
group_builder.specify "Complex plus Integer" <|
|
||||
v1 = (Complex.new 1 2) + (Complex.new 3)
|
||||
@ -623,7 +639,13 @@ add_specs suite_builder =
|
||||
v1>v2 . should_be_false
|
||||
v2>v1 . should_be_true
|
||||
|
||||
group_builder.specify "Greater or equal of complex and number" <|
|
||||
v1<=v2 . should_be_true
|
||||
v2<=v1 . should_be_false
|
||||
|
||||
v1>=v2 . should_be_false
|
||||
v2>=v1 . should_be_true
|
||||
|
||||
group_builder.specify "Greater or equal of complex < number" <|
|
||||
v1 = (Complex.new 3)
|
||||
v2 = 4
|
||||
v1<v2 . should_be_true
|
||||
@ -632,7 +654,13 @@ add_specs suite_builder =
|
||||
v1>v2 . should_be_false
|
||||
v2>v1 . should_be_true
|
||||
|
||||
group_builder.specify "Greater or equal of number and complex" <|
|
||||
v1<=v2 . should_be_true
|
||||
v2<=v1 . should_be_false
|
||||
|
||||
v1>=v2 . should_be_false
|
||||
v2>=v1 . should_be_true
|
||||
|
||||
group_builder.specify "Greater or equal of complex > number" <|
|
||||
v1 = 3
|
||||
v2 = (Complex.new 4)
|
||||
v1<v2 . should_be_true
|
||||
@ -641,6 +669,27 @@ add_specs suite_builder =
|
||||
v1>v2 . should_be_false
|
||||
v2>v1 . should_be_true
|
||||
|
||||
v1<=v2 . should_be_true
|
||||
v2<=v1 . should_be_false
|
||||
|
||||
v1>=v2 . should_be_false
|
||||
v2>=v1 . should_be_true
|
||||
|
||||
group_builder.specify "Greater or equal of complex == number" <|
|
||||
v1 = 4
|
||||
v2 = (Complex.new 4)
|
||||
v1<v2 . should_be_false
|
||||
v2<v1 . should_be_false
|
||||
|
||||
v1>v2 . should_be_false
|
||||
v2>v1 . should_be_false
|
||||
|
||||
v1<=v2 . should_be_true
|
||||
v2<=v1 . should_be_true
|
||||
|
||||
v1>=v2 . should_be_true
|
||||
v2>=v1 . should_be_true
|
||||
|
||||
suite_builder.group "BigInts" group_builder->
|
||||
expected_value = 2002115494039257055317447151023464523838443110452722331466645440244415760562579268801894716412
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user