Skip to content

Commit

Permalink
Add round, ceil, floor, truncate to the In-Database Column type (enso…
Browse files Browse the repository at this point in the history
  • Loading branch information
GregoryTravis authored Jun 30, 2023
1 parent 6eac095 commit 550d146
Show file tree
Hide file tree
Showing 26 changed files with 1,130 additions and 134 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -496,8 +496,10 @@
- [Speed improvements to `Column` `.truncate`, `.ceil`, and `.floor`.][6941]
- [Implemented addition and subtraction for `Date_Period` and
`Time_Period`.][6956]
- [Implemented `Table.update_database_table`.][7035]
- [Added AWS credential support and initial S3 list buckets API.][6973]
- [Added `round`, `ceil`, `floor`, `truncate` to the In-Database Column type]
[6988]
- [Implemented `Table.update_database_table`.][7035]
- [Removed `module` argument from `enso_project` and other minor tweaks.][7052]
- [Integrated Database write operations with Execution Contexts.][7072]

Expand Down Expand Up @@ -720,6 +722,7 @@
[6941]: https://github.com/enso-org/enso/pull/6941
[6956]: https://github.com/enso-org/enso/pull/6956
[6973]: https://github.com/enso-org/enso/pull/6973
[6988]: https://github.com/enso-org/enso/pull/6988
[7035]: https://github.com/enso-org/enso/pull/7035
[7052]: https://github.com/enso-org/enso/pull/7052
[7072]: https://github.com/enso-org/enso/pull/7072
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ type Redshift_Dialect
supports_separate_nan : Boolean
supports_separate_nan self = True

## PRIVATE
supports_negative_round_decimal_places : Boolean
supports_negative_round_decimal_places self = True

## PRIVATE
rounding_decimal_places_not_allowed_for_floats : Boolean
rounding_decimal_places_not_allowed_for_floats self = True

## PRIVATE
adapt_unified_column : Internal_Column -> Value_Type -> (SQL_Expression -> SQL_Type_Reference) -> Internal_Column
adapt_unified_column self column approximate_result_type infer_result_type_from_database_callback =
Expand Down
55 changes: 7 additions & 48 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import project.Any.Any
import project.Internal.Rounding_Helpers
import project.Data.Text.Text
import project.Data.Locale.Locale
import project.Errors.Common.Arithmetic_Error
Expand Down Expand Up @@ -609,20 +610,20 @@ type Decimal
2.5 . round use_bankers=True == 2
round : Integer -> Boolean -> Integer | Decimal ! Illegal_Argument
round self decimal_places=0 use_bankers=False =
check_decimal_places decimal_places <|
Rounding_Helpers.check_decimal_places decimal_places <|
case self.is_nan || self.is_infinite of
True ->
msg = "round cannot accept " + self.to_text
Error.throw (Arithmetic_Error.Error msg)
False -> check_round_input self <|
False -> Rounding_Helpers.check_round_input self <|
decimal_result =
# Algorithm taken from https://stackoverflow.com/a/7211688
scale = 10 ^ decimal_places
scaled = self * scale
round_base = scaled.floor
round_midpoint = (round_base + 0.5) / scale
even_is_up = if self >= 0 then (scaled.truncate % 2) != 0 else (scaled.truncate % 2) == 0
half_goes_up = if use_bankers then even_is_up else True
half_goes_up = if use_bankers then even_is_up else self >= 0
do_round_up = if half_goes_up then self >= round_midpoint else self > round_midpoint
if do_round_up then ((round_base + 1.0) / scale) else (round_base / scale)
# Convert to integer if it's really an integer anyway.
Expand Down Expand Up @@ -936,19 +937,19 @@ type Integer
## It's already an integer so unless decimal_places is
negative, the value is unchanged.
if decimal_places >= 0 then self else
check_decimal_places decimal_places <| check_round_input self <|
Rounding_Helpers.check_decimal_places decimal_places <| Rounding_Helpers.check_round_input self <|
scale = 10 ^ -decimal_places
halfway = scale.div 2
remainder = self % scale
scaled_down = self.div scale
result_unnudged = scaled_down * scale
case self >= 0 of
True ->
half_goes_up = if use_bankers then (scaled_down % 2) != 0 else True
half_goes_up = if use_bankers then (scaled_down % 2) != 0 else self >= 0
round_up = if half_goes_up then remainder >= halfway else remainder > halfway
if round_up then result_unnudged + scale else result_unnudged
False ->
half_goes_up = if use_bankers then (scaled_down % 2) == 0 else True
half_goes_up = if use_bankers then (scaled_down % 2) == 0 else self >= 0
round_up = if half_goes_up then remainder < -halfway else remainder <= -halfway
if round_up then result_unnudged - scale else result_unnudged

Expand Down Expand Up @@ -1119,45 +1120,3 @@ type Number_Parse_Error
to_display_text : Text
to_display_text self =
"Could not parse " + self.text.to_text + " as a double."

## PRIVATE
The smallest allowed value for the `decimal_places` argument to `round`
round_min_decimal_places : Integer
round_min_decimal_places = -15

## PRIVATE
The largest allowed value for the `decimal_places` argument to `round`
round_max_decimal_places : Integer
round_max_decimal_places = 15

## PRIVATE
The largest smallInteger (Long) that integer round can handle. Above 14
digits, it is possible that the underlying long, converted to double in the
rounding process, would lose precision in the least significant bits.
(See https://en.wikipedia.org/wiki/Double-precision_floating-point_format.)
round_max_long : Integer
round_max_long = 99999999999999

## PRIVATE
The largest smallInteger (Long) that integer round can handle. Above 14
digits, it is possible that the underlying long, converted to double in the
rounding process, would lose precision in the least significant bits.
(See https://en.wikipedia.org/wiki/Double-precision_floating-point_format.)
round_min_long : Integer
round_min_long = -99999999999999

## PRIVATE
Restrict rounding decimal_places parameter.
check_decimal_places : Integer -> Any -> Any ! Illegal_Argument
check_decimal_places decimal_places ~action =
if decimal_places >= round_min_decimal_places && decimal_places <= round_max_decimal_places then action else
msg = "round: decimal_places must be between " + round_min_decimal_places.to_text + " and " + round_max_decimal_places.to_text + " (inclusive), but was " + decimal_places.to_text
Error.throw (Illegal_Argument.Error msg)

## PRIVATE
Restrict allowed range of input to rounding methods.
check_round_input : Number -> Function -> Any ! Illegal_Argument
check_round_input n ~action =
if n >= round_min_long && n <= round_max_long then action else
msg = "Error: `round` can only accept values between " + round_min_long.to_text + " and " + round_max_long.to_text + " (inclusive), but was " + n.to_text
Error.throw (Illegal_Argument.Error msg)
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import project.Any.Any
import project.Data.Numbers.Number
import project.Data.Numbers.Integer
import project.Error.Error
import project.Errors.Illegal_Argument.Illegal_Argument
import project.Function.Function

## PRIVATE
The largest smallInteger (Long) that integer round can handle. Above 14
digits, it is possible that the underlying long, converted to double in the
rounding process, would lose precision in the least significant bits.
(See https://en.wikipedia.org/wiki/Double-precision_floating-point_format.)
round_max_long : Integer
round_max_long = 99999999999999

## PRIVATE
The largest smallInteger (Long) that integer round can handle. Above 14
digits, it is possible that the underlying long, converted to double in the
rounding process, would lose precision in the least significant bits.
(See https://en.wikipedia.org/wiki/Double-precision_floating-point_format.)
round_min_long : Integer
round_min_long = -99999999999999

## PRIVATE
Restrict allowed range of input to rounding methods.
check_round_input : Number -> Function -> Any ! Illegal_Argument
check_round_input n ~action =
if n >= round_min_long && n <= round_max_long then action else
msg = "Error: `round` can only accept values between " + round_min_long.to_text + " and " + round_max_long.to_text + " (inclusive), but was " + n.to_text
Error.throw (Illegal_Argument.Error msg)

## PRIVATE
The smallest allowed value for the `decimal_places` argument to `round`
round_min_decimal_places : Integer
round_min_decimal_places = -15

## PRIVATE
The largest allowed value for the `decimal_places` argument to `round`
round_max_decimal_places : Integer
round_max_decimal_places = 15

## PRIVATE
Restrict rounding decimal_places parameter.
check_decimal_places : Integer -> Any -> Any ! Illegal_Argument
check_decimal_places decimal_places ~action =
if decimal_places >= round_min_decimal_places && decimal_places <= round_max_decimal_places then action else
msg = "round: decimal_places must be between " + round_min_decimal_places.to_text + " and " + round_max_decimal_places.to_text + " (inclusive), but was " + decimal_places.to_text
Error.throw (Illegal_Argument.Error msg)
2 changes: 2 additions & 0 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import project.Data.Vector.Vector
import project.Error.Error
import project.Errors
import project.Function
import project.Internal.Rounding_Helpers
import project.IO
import project.Math
import project.Meta
Expand Down Expand Up @@ -44,6 +45,7 @@ export project.Data.Text.Text
export project.Data.Vector.Vector
export project.Error.Error
export project.Errors
export project.Internal.Rounding_Helpers
export project.IO
export project.Math
export project.Meta
Expand Down
Loading

0 comments on commit 550d146

Please sign in to comment.