mirror of
https://github.com/enso-org/enso.git
synced 2024-09-11 13:15:52 +03:00
Add Decimal.round
(#9672)
This commit is contained in:
parent
bf90e2e8a8
commit
e3afa5561d
@ -648,6 +648,7 @@
|
||||
- [Added `Decimal.parse` and `.format`.][9637]
|
||||
- [Added `Decimal.abs`, `.negate` and `.signum`.][9641]
|
||||
- [Added `Decimal.min` and `.max`.][9663]
|
||||
- [Added `Decimal.round`.][9672]
|
||||
|
||||
[debug-shortcuts]:
|
||||
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
|
||||
@ -946,6 +947,7 @@
|
||||
[9637]: https://github.com/enso-org/enso/pull/9637
|
||||
[9641]: https://github.com/enso-org/enso/pull/9641
|
||||
[9663]: https://github.com/enso-org/enso/pull/9663
|
||||
[9672]: https://github.com/enso-org/enso/pull/9672
|
||||
|
||||
#### Enso Compiler
|
||||
|
||||
|
@ -18,6 +18,7 @@ from project.Widget_Helpers import make_number_format_selector
|
||||
|
||||
polyglot java import java.lang.ArithmeticException
|
||||
polyglot java import java.lang.NumberFormatException
|
||||
polyglot java import java.lang.Integer as Java_Integer
|
||||
polyglot java import java.math.BigDecimal
|
||||
polyglot java import java.text.DecimalFormat
|
||||
polyglot java import java.text.DecimalFormatSymbols
|
||||
@ -794,6 +795,61 @@ type Decimal
|
||||
message = "Outside representable Float range (approximately (-1.8E308, 1.8E308))"
|
||||
Warning.attach (Out_Of_Range.Error self message) f
|
||||
|
||||
## GROUP Rounding
|
||||
ICON math
|
||||
Round to a specified number of decimal places.
|
||||
|
||||
By default, rounding uses "symmetric round-half-up", also known as
|
||||
"half-up." If use_bankers=True, then it uses "round-half-even", also
|
||||
known as "banker's rounding".
|
||||
|
||||
Arguments:
|
||||
- decimal_places: The number of decimal places to round to. Can be
|
||||
negative, which results in rounding to positive integer powers of 10.
|
||||
Must be between Java `Integer.MIN_VALUE` and `Integer.MAX_VALUE`
|
||||
(-2147483648 and 2147483647) (inclusive).
|
||||
- use_bankers: Rounds mid-point to nearest even number.
|
||||
|
||||
! Error Conditions
|
||||
|
||||
If `decimal_places` is outside the range `Integer.MIN_VALUE` and
|
||||
`Integer.MAX_VALUE` (inclusive), an `Illegal_Argument` error is thrown.
|
||||
|
||||
? Negative decimal place counts
|
||||
Rounding to `n` digits can be thought of as "rounding to the nearest
|
||||
multiple of 10^(-n)". For negative decimal counts, this results in
|
||||
rounding to the nearest positive integer power of 10.
|
||||
|
||||
> Example
|
||||
Round to the nearest integer.
|
||||
|
||||
Decimal.new "3.3" . round
|
||||
# => Decimal.new "3"
|
||||
|
||||
> Example
|
||||
Round to two decimal places.
|
||||
|
||||
Decimal.new "3.1415" . round 2
|
||||
# => Decimal.new "3.14"
|
||||
|
||||
> Example
|
||||
Round a very large number.
|
||||
|
||||
Decimal.new "1234.5678E-50" . round 53
|
||||
# => Decimal.new "1234.568E-50"
|
||||
|
||||
> Example
|
||||
Use Banker's Rounding.
|
||||
|
||||
Decimal.new "2.5" . round use_bankers=True
|
||||
# => 2
|
||||
round : Integer -> Boolean -> Decimal
|
||||
round self (decimal_places:Integer=0) (use_bankers:Boolean=False) -> Decimal =
|
||||
out_of_range = decimal_places > Java_Integer.MAX_VALUE || decimal_places < Java_Integer.MIN_VALUE
|
||||
if out_of_range.not then Decimal.Value (Decimal_Utils.round self.big_decimal decimal_places use_bankers) else
|
||||
message = "round decimal_places must be between "+Java_Integer.MIN_VALUE.to_text+" and "+Java_Integer.MAX_VALUE.to_text+" (inclusive), but was "+decimal_places.to_text
|
||||
Error.throw (Out_Of_Range.Error decimal_places message)
|
||||
|
||||
## GROUP Conversions
|
||||
ICON convert
|
||||
Converts a `Decimal` to a string, using the Java `DecimalFormat` formatter.
|
||||
|
@ -655,9 +655,9 @@ type Float
|
||||
ICON math
|
||||
Round to a specified number of decimal places.
|
||||
|
||||
By default, rounding uses "asymmetric round-half-up", also known as
|
||||
"round towards positive infinity." If use_bankers=True, then it uses
|
||||
"round-half-even", also known as "banker's rounding".
|
||||
By default, rounding uses "symmetric round-half-up", also known as
|
||||
"half-up." If use_bankers=True, then it uses "round-half-even", also
|
||||
known as "banker's rounding".
|
||||
|
||||
If `decimal_places` > 0, `round` returns a `Float`; otherwise, it
|
||||
returns an `Integer`.
|
||||
@ -1017,9 +1017,9 @@ type Integer
|
||||
For integers, rounding to 0 or more decimal places simply returns the
|
||||
argument. For negative decimal places, see below.
|
||||
|
||||
By default, rounding uses "asymmetric round-half-up", also known as
|
||||
"round towards positive infinity." If use_bankers=True, then it uses
|
||||
"round-half-even", also known as "banker's rounding".
|
||||
By default, rounding uses "symmetric round-half-up", also known as
|
||||
"half-up." If use_bankers=True, then it uses "round-half-even", also
|
||||
known as "banker's rounding".
|
||||
|
||||
Arguments:
|
||||
- decimal_places: The number of decimal places to round to. Can be
|
||||
|
@ -3,6 +3,7 @@ package org.enso.base.numeric;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/** Utils for the Enso Decmial type. */
|
||||
public class Decimal_Utils {
|
||||
@ -91,4 +92,12 @@ public class Decimal_Utils {
|
||||
return bd.toBigIntegerExact().hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static BigDecimal round(BigDecimal bd, int decimalPlaces, boolean useBankers) {
|
||||
var roundingMode = useBankers ? RoundingMode.HALF_EVEN : RoundingMode.HALF_UP;
|
||||
// `stripTrailingZeros` is necessary because rounding can produce results
|
||||
// that have extra trailing zeros, which take up space and slow down later
|
||||
// calculations.
|
||||
return bd.setScale(decimalPlaces, roundingMode).stripTrailingZeros();
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,10 @@ from Standard.Base.Errors.Common import Loss_Of_Numeric_Precision, Out_Of_Range
|
||||
|
||||
from Standard.Test import all
|
||||
|
||||
import project.Data.Round_Spec
|
||||
|
||||
polyglot java import java.lang.Integer as Java_Integer
|
||||
|
||||
Decimal.should_have_rep self rep = self.internal_representation . should_equal rep
|
||||
|
||||
add_specs suite_builder =
|
||||
@ -729,6 +733,36 @@ add_specs suite_builder =
|
||||
Decimal.new "-12.345E97" . signum . should_equal -1
|
||||
Decimal.new "0" . signum . should_equal 0
|
||||
|
||||
suite_builder.group "rounding" group_builder->
|
||||
do_round n dp=0 use_bankers=False = Decimal.new n . round dp use_bankers
|
||||
Round_Spec.add_specs group_builder do_round
|
||||
|
||||
group_builder.specify "Large values" <|
|
||||
Decimal.new "1234.5678E-50" . round 53 . should_equal (Decimal.new "1234.568E-50")
|
||||
Decimal.new "1234.5678E-50" . round 52 . should_equal (Decimal.new "1234.57E-50")
|
||||
Decimal.new "1234.5678E-100" . round 101 . should_equal (Decimal.new "1234.6E-100")
|
||||
Decimal.new "1234.5678E-100" . round 103 . should_equal (Decimal.new "1234.568E-100")
|
||||
|
||||
Decimal.new "-1234.5678E-50" . round 53 . should_equal (Decimal.new "-1234.568E-50")
|
||||
Decimal.new "-1234.5678E-50" . round 52 . should_equal (Decimal.new "-1234.57E-50")
|
||||
Decimal.new "-1234.5678E-100" . round 101 . should_equal (Decimal.new "-1234.6E-100")
|
||||
Decimal.new "-1234.5678E-100" . round 103 . should_equal (Decimal.new "-1234.568E-100")
|
||||
|
||||
group_builder.specify "Strips trailing zeros" <|
|
||||
Decimal.new "1234.5678" . round 5 . internal_representation . should_equal [12345678, 8, 4]
|
||||
|
||||
group_builder.specify "Decimal places out of range" <|
|
||||
too_big_positive = Java_Integer.MAX_VALUE + 1
|
||||
too_big_negative = Java_Integer.MIN_VALUE - 1
|
||||
Decimal.new "3.1" . round too_big_positive . should_fail_with (Out_Of_Range.Error too_big_positive "round decimal_places must be between "+Java_Integer.MIN_VALUE.to_text+" and "+Java_Integer.MAX_VALUE.to_text+" (inclusive), but was "+too_big_positive.to_text)
|
||||
Decimal.new "3.1" . round too_big_negative . should_fail_with (Out_Of_Range.Error too_big_negative "round decimal_places must be between "+Java_Integer.MIN_VALUE.to_text+" and "+Java_Integer.MAX_VALUE.to_text+" (inclusive), but was "+too_big_negative.to_text)
|
||||
|
||||
group_builder.specify "Examples" <|
|
||||
Decimal.new "3.3" . round . should_equal (Decimal.new "3")
|
||||
Decimal.new "3.1415" . round 2 . should_equal (Decimal.new "3.14")
|
||||
Decimal.new "1234.5678E-50" . round 53 . should_equal (Decimal.new "1234.568E-50")
|
||||
Decimal.new "2.5" . round use_bankers=True . should_equal 2
|
||||
|
||||
suite_builder.group "min/max" group_builder->
|
||||
group_builder.specify "should calculate min and max correctly" <|
|
||||
Decimal.new "12" . min (Decimal.new "13") . should_equal (Decimal.new "12")
|
||||
|
@ -61,8 +61,6 @@ add_specs suite_builder =
|
||||
very_negative = -99223372036854775808
|
||||
|
||||
suite_builder.group "Integers" group_builder->
|
||||
Round_Spec.add_specs group_builder (.round)
|
||||
|
||||
group_builder.specify "should be of unbound size when multiplied" <|
|
||||
1.up_to 101 . fold 1 (*) . should_equal hundred_factorial
|
||||
|
||||
@ -605,6 +603,13 @@ add_specs suite_builder =
|
||||
(922337203685477580712345 - 922337203685477580700000) . round . should_equal 12345
|
||||
((99999999999998 * 1000).div 1000) . round . should_equal 99999999999998
|
||||
|
||||
suite_builder.group "Rounding" group_builder->
|
||||
Round_Spec.add_specs group_builder (.round)
|
||||
|
||||
group_builder.specify "Decimal places out of range" <|
|
||||
3.1 . round 16 . should_fail_with Illegal_Argument
|
||||
3.1 . round -16 . should_fail_with Illegal_Argument
|
||||
|
||||
suite_builder.group "Float.truncate" group_builder->
|
||||
|
||||
group_builder.specify "Correctly converts to Integer" <|
|
||||
|
@ -178,10 +178,6 @@ add_specs group_builder round_fun =
|
||||
round_fun -1.22222222222235 13 use_bankers=True . should_equal -1.2222222222224
|
||||
round_fun -1.222222222222235 14 use_bankers=True . should_equal -1.22222222222224
|
||||
|
||||
group_builder.specify "Decimal places out of range" <|
|
||||
round_fun 3.1 16 . should_fail_with Illegal_Argument
|
||||
round_fun 3.1 -16 . should_fail_with Illegal_Argument
|
||||
|
||||
group_builder.specify "Floating point imperfect representation counter-examples" <|
|
||||
round_fun 1.225 2 use_bankers=True . should_equal 1.22 # Actual result 1.23
|
||||
round_fun 37.785 2 . should_equal 37.79
|
||||
|
@ -181,6 +181,10 @@ add_specs suite_builder setup =
|
||||
|
||||
Round_Spec.add_specs group_builder do_round
|
||||
|
||||
group_builder.specify "Decimal places out of range" <|
|
||||
3.1 . round 16 . should_fail_with Illegal_Argument
|
||||
3.1 . round -16 . should_fail_with Illegal_Argument
|
||||
|
||||
## Runs the provided callback with a few combinations of columns, where some
|
||||
of them are made Mixed (but still contain only the original values).
|
||||
If the backend does not support mixed columns, the callback is run only
|
||||
|
Loading…
Reference in New Issue
Block a user