Fix rounding functions and tests

This commit is contained in:
NicklasXYZ 2024-08-12 22:55:26 +02:00
parent 42c4ae114d
commit 7ecaad5be2
3 changed files with 116 additions and 134 deletions

View file

@ -112,7 +112,7 @@ import gleam_community/maths/elementary
/// </a>
/// </div>
///
pub fn ceiling(x: Float, digits: option.Option(Int)) -> Result(Float, String) {
pub fn ceiling(x: Float, digits: option.Option(Int)) -> Float {
round(x, digits, option.Some(RoundUp))
}
@ -167,7 +167,7 @@ pub fn ceiling(x: Float, digits: option.Option(Int)) -> Result(Float, String) {
/// </a>
/// </div>
///
pub fn floor(x: Float, digits: option.Option(Int)) -> Result(Float, String) {
pub fn floor(x: Float, digits: option.Option(Int)) -> Float {
round(x, digits, option.Some(RoundDown))
}
@ -222,7 +222,7 @@ pub fn floor(x: Float, digits: option.Option(Int)) -> Result(Float, String) {
/// </a>
/// </div>
///
pub fn truncate(x: Float, digits: option.Option(Int)) -> Result(Float, String) {
pub fn truncate(x: Float, digits: option.Option(Int)) -> Float {
round(x, digits, option.Some(RoundToZero))
}
@ -360,7 +360,7 @@ pub fn round(
x: Float,
digits: option.Option(Int),
mode: option.Option(RoundingMode),
) -> Result(Float, String) {
) -> Float {
case digits {
option.Some(a) -> {
let assert Ok(p) = elementary.power(10.0, conversion.int_to_float(a))
@ -381,35 +381,17 @@ pub type RoundingMode {
RoundUp
}
fn do_round(
p: Float,
x: Float,
mode: option.Option(RoundingMode),
) -> Result(Float, String) {
fn do_round(p: Float, x: Float, mode: option.Option(RoundingMode)) -> Float {
case mode {
// Determine the rounding mode
option.Some(RoundNearest) ->
round_to_nearest(p, x)
|> Ok
option.Some(RoundTiesAway) ->
round_ties_away(p, x)
|> Ok
option.Some(RoundTiesUp) ->
round_ties_up(p, x)
|> Ok
option.Some(RoundToZero) ->
round_to_zero(p, x)
|> Ok
option.Some(RoundDown) ->
round_down(p, x)
|> Ok
option.Some(RoundUp) ->
round_up(p, x)
|> Ok
option.Some(RoundNearest) -> round_to_nearest(p, x)
option.Some(RoundTiesAway) -> round_ties_away(p, x)
option.Some(RoundTiesUp) -> round_ties_up(p, x)
option.Some(RoundToZero) -> round_to_zero(p, x)
option.Some(RoundDown) -> round_down(p, x)
option.Some(RoundUp) -> round_up(p, x)
// Otherwise, use the default rounding mode
option.None ->
round_to_nearest(p, x)
|> Ok
option.None -> round_to_nearest(p, x)
}
}

View file

@ -247,9 +247,9 @@ fn do_ceiling(a: Float) -> Float
pub fn is_power(x: Int, y: Int) -> Bool {
let assert Ok(value) =
elementary.logarithm(int.to_float(x), option.Some(int.to_float(y)))
let assert Ok(truncated) = piecewise.truncate(value, option.Some(0))
let rem = value -. truncated
rem == 0.0
let truncated = piecewise.truncate(value, option.Some(0))
let remainder = value -. truncated
remainder == 0.0
}
/// <div style="text-align: right;">

View file

@ -7,308 +7,308 @@ import gleeunit/should
pub fn float_ceiling_test() {
// Round 3. digit AFTER decimal point
piecewise.ceiling(12.0654, option.Some(3))
|> should.equal(Ok(12.066))
|> should.equal(12.066)
// Round 2. digit AFTER decimal point
piecewise.ceiling(12.0654, option.Some(2))
|> should.equal(Ok(12.07))
|> should.equal(12.07)
// Round 1. digit AFTER decimal point
piecewise.ceiling(12.0654, option.Some(1))
|> should.equal(Ok(12.1))
|> should.equal(12.1)
// Round 0. digit BEFORE decimal point
piecewise.ceiling(12.0654, option.Some(0))
|> should.equal(Ok(13.0))
|> should.equal(13.0)
// Round 1. digit BEFORE decimal point
piecewise.ceiling(12.0654, option.Some(-1))
|> should.equal(Ok(20.0))
|> should.equal(20.0)
// Round 2. digit BEFORE decimal point
piecewise.ceiling(12.0654, option.Some(-2))
|> should.equal(Ok(100.0))
|> should.equal(100.0)
// Round 3. digit BEFORE decimal point
piecewise.ceiling(12.0654, option.Some(-3))
|> should.equal(Ok(1000.0))
|> should.equal(1000.0)
}
pub fn float_floor_test() {
// Round 3. digit AFTER decimal point
piecewise.floor(12.0654, option.Some(3))
|> should.equal(Ok(12.065))
|> should.equal(12.065)
// Round 2. digit AFTER decimal point
piecewise.floor(12.0654, option.Some(2))
|> should.equal(Ok(12.06))
|> should.equal(12.06)
// Round 1. digit AFTER decimal point
piecewise.floor(12.0654, option.Some(1))
|> should.equal(Ok(12.0))
|> should.equal(12.0)
// Round 0. digit BEFORE decimal point
piecewise.floor(12.0654, option.Some(0))
|> should.equal(Ok(12.0))
|> should.equal(12.0)
// Round 1. digit BEFORE decimal point
piecewise.floor(12.0654, option.Some(-1))
|> should.equal(Ok(10.0))
|> should.equal(10.0)
// Round 2. digit BEFORE decimal point
piecewise.floor(12.0654, option.Some(-2))
|> should.equal(Ok(0.0))
|> should.equal(0.0)
// Round 2. digit BEFORE decimal point
piecewise.floor(12.0654, option.Some(-3))
|> should.equal(Ok(0.0))
|> should.equal(0.0)
}
pub fn float_truncate_test() {
// Round 3. digit AFTER decimal point
piecewise.truncate(12.0654, option.Some(3))
|> should.equal(Ok(12.065))
|> should.equal(12.065)
// Round 2. digit AFTER decimal point
piecewise.truncate(12.0654, option.Some(2))
|> should.equal(Ok(12.06))
|> should.equal(12.06)
// Round 1. digit AFTER decimal point
piecewise.truncate(12.0654, option.Some(1))
|> should.equal(Ok(12.0))
|> should.equal(12.0)
// Round 0. digit BEFORE decimal point
piecewise.truncate(12.0654, option.Some(0))
|> should.equal(Ok(12.0))
|> should.equal(12.0)
// Round 1. digit BEFORE decimal point
piecewise.truncate(12.0654, option.Some(-1))
|> should.equal(Ok(10.0))
|> should.equal(10.0)
// Round 2. digit BEFORE decimal point
piecewise.truncate(12.0654, option.Some(-2))
|> should.equal(Ok(0.0))
|> should.equal(0.0)
// Round 2. digit BEFORE decimal point
piecewise.truncate(12.0654, option.Some(-3))
|> should.equal(Ok(0.0))
|> should.equal(0.0)
}
pub fn math_round_to_nearest_test() {
// Try with positive values
piecewise.round(1.5, option.Some(0), option.Some(piecewise.RoundNearest))
|> should.equal(Ok(2.0))
|> should.equal(2.0)
piecewise.round(1.75, option.Some(0), option.Some(piecewise.RoundNearest))
|> should.equal(Ok(2.0))
|> should.equal(2.0)
piecewise.round(2.0, option.Some(0), option.Some(piecewise.RoundNearest))
|> should.equal(Ok(2.0))
|> should.equal(2.0)
piecewise.round(3.5, option.Some(0), option.Some(piecewise.RoundNearest))
|> should.equal(Ok(4.0))
|> should.equal(4.0)
piecewise.round(4.5, option.Some(0), option.Some(piecewise.RoundNearest))
|> should.equal(Ok(4.0))
|> should.equal(4.0)
// Try with negative values
piecewise.round(-3.5, option.Some(0), option.Some(piecewise.RoundNearest))
|> should.equal(Ok(-4.0))
|> should.equal(-4.0)
piecewise.round(-4.5, option.Some(0), option.Some(piecewise.RoundNearest))
|> should.equal(Ok(-4.0))
|> should.equal(-4.0)
// Round 3. digit AFTER decimal point
piecewise.round(12.0654, option.Some(3), option.Some(piecewise.RoundNearest))
|> should.equal(Ok(12.065))
|> should.equal(12.065)
// Round 2. digit AFTER decimal point
piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundNearest))
|> should.equal(Ok(12.07))
|> should.equal(12.07)
// Round 1. digit AFTER decimal point
piecewise.round(12.0654, option.Some(1), option.Some(piecewise.RoundNearest))
|> should.equal(Ok(12.1))
|> should.equal(12.1)
// Round 0. digit BEFORE decimal point
piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundNearest))
|> should.equal(Ok(12.0))
|> should.equal(12.0)
// Round 1. digit BEFORE decimal point
piecewise.round(12.0654, option.Some(-1), option.Some(piecewise.RoundNearest))
|> should.equal(Ok(10.0))
|> should.equal(10.0)
// Round 2. digit BEFORE decimal point
piecewise.round(12.0654, option.Some(-2), option.Some(piecewise.RoundNearest))
|> should.equal(Ok(0.0))
|> should.equal(0.0)
// Round 3. digit BEFORE decimal point
piecewise.round(12.0654, option.Some(-3), option.Some(piecewise.RoundNearest))
|> should.equal(Ok(0.0))
|> should.equal(0.0)
}
pub fn math_round_up_test() {
// Note: Rounding mode "RoundUp" is an alias for the ceiling function
// Try with positive values
piecewise.round(0.45, option.Some(0), option.Some(piecewise.RoundUp))
|> should.equal(Ok(1.0))
|> should.equal(1.0)
piecewise.round(0.5, option.Some(0), option.Some(piecewise.RoundUp))
|> should.equal(Ok(1.0))
|> should.equal(1.0)
piecewise.round(0.45, option.Some(1), option.Some(piecewise.RoundUp))
|> should.equal(Ok(0.5))
|> should.equal(0.5)
piecewise.round(0.5, option.Some(1), option.Some(piecewise.RoundUp))
|> should.equal(Ok(0.5))
|> should.equal(0.5)
piecewise.round(0.455, option.Some(2), option.Some(piecewise.RoundUp))
|> should.equal(Ok(0.46))
|> should.equal(0.46)
piecewise.round(0.505, option.Some(2), option.Some(piecewise.RoundUp))
|> should.equal(Ok(0.51))
|> should.equal(0.51)
// Try with negative values
piecewise.round(-0.45, option.Some(0), option.Some(piecewise.RoundUp))
|> should.equal(Ok(-0.0))
|> should.equal(-0.0)
piecewise.round(-0.5, option.Some(0), option.Some(piecewise.RoundUp))
|> should.equal(Ok(-0.0))
|> should.equal(-0.0)
piecewise.round(-0.45, option.Some(1), option.Some(piecewise.RoundUp))
|> should.equal(Ok(-0.4))
|> should.equal(-0.4)
piecewise.round(-0.5, option.Some(1), option.Some(piecewise.RoundUp))
|> should.equal(Ok(-0.5))
|> should.equal(-0.5)
piecewise.round(-0.455, option.Some(2), option.Some(piecewise.RoundUp))
|> should.equal(Ok(-0.45))
|> should.equal(-0.45)
piecewise.round(-0.505, option.Some(2), option.Some(piecewise.RoundUp))
|> should.equal(Ok(-0.5))
|> should.equal(-0.5)
}
pub fn math_round_down_test() {
// Note: Rounding mode "RoundDown" is an alias for the floor function
// Try with positive values
piecewise.round(0.45, option.Some(0), option.Some(piecewise.RoundDown))
|> should.equal(Ok(0.0))
|> should.equal(0.0)
piecewise.round(0.5, option.Some(0), option.Some(piecewise.RoundDown))
|> should.equal(Ok(0.0))
|> should.equal(0.0)
piecewise.round(0.45, option.Some(1), option.Some(piecewise.RoundDown))
|> should.equal(Ok(0.4))
|> should.equal(0.4)
piecewise.round(0.5, option.Some(1), option.Some(piecewise.RoundDown))
|> should.equal(Ok(0.5))
|> should.equal(0.5)
piecewise.round(0.455, option.Some(2), option.Some(piecewise.RoundDown))
|> should.equal(Ok(0.45))
|> should.equal(0.45)
piecewise.round(0.505, option.Some(2), option.Some(piecewise.RoundDown))
|> should.equal(Ok(0.5))
|> should.equal(0.5)
// Try with negative values
piecewise.round(-0.45, option.Some(0), option.Some(piecewise.RoundDown))
|> should.equal(Ok(-1.0))
|> should.equal(-1.0)
piecewise.round(-0.5, option.Some(0), option.Some(piecewise.RoundDown))
|> should.equal(Ok(-1.0))
|> should.equal(-1.0)
piecewise.round(-0.45, option.Some(1), option.Some(piecewise.RoundDown))
|> should.equal(Ok(-0.5))
|> should.equal(-0.5)
piecewise.round(-0.5, option.Some(1), option.Some(piecewise.RoundDown))
|> should.equal(Ok(-0.5))
|> should.equal(-0.5)
piecewise.round(-0.455, option.Some(2), option.Some(piecewise.RoundDown))
|> should.equal(Ok(-0.46))
|> should.equal(-0.46)
piecewise.round(-0.505, option.Some(2), option.Some(piecewise.RoundDown))
|> should.equal(Ok(-0.51))
|> should.equal(-0.51)
}
pub fn math_round_to_zero_test() {
// Note: Rounding mode "RoundToZero" is an alias for the truncate function
// Try with positive values
piecewise.round(0.5, option.Some(0), option.Some(piecewise.RoundToZero))
|> should.equal(Ok(0.0))
|> should.equal(0.0)
piecewise.round(0.75, option.Some(0), option.Some(piecewise.RoundToZero))
|> should.equal(Ok(0.0))
|> should.equal(0.0)
piecewise.round(0.45, option.Some(1), option.Some(piecewise.RoundToZero))
|> should.equal(Ok(0.4))
|> should.equal(0.4)
piecewise.round(0.57, option.Some(1), option.Some(piecewise.RoundToZero))
|> should.equal(Ok(0.5))
|> should.equal(0.5)
piecewise.round(0.4575, option.Some(2), option.Some(piecewise.RoundToZero))
|> should.equal(Ok(0.45))
|> should.equal(0.45)
piecewise.round(0.5075, option.Some(2), option.Some(piecewise.RoundToZero))
|> should.equal(Ok(0.5))
|> should.equal(0.5)
// Try with negative values
piecewise.round(-0.5, option.Some(0), option.Some(piecewise.RoundToZero))
|> should.equal(Ok(0.0))
|> should.equal(0.0)
piecewise.round(-0.75, option.Some(0), option.Some(piecewise.RoundToZero))
|> should.equal(Ok(0.0))
|> should.equal(0.0)
piecewise.round(-0.45, option.Some(1), option.Some(piecewise.RoundToZero))
|> should.equal(Ok(-0.4))
|> should.equal(-0.4)
piecewise.round(-0.57, option.Some(1), option.Some(piecewise.RoundToZero))
|> should.equal(Ok(-0.5))
|> should.equal(-0.5)
piecewise.round(-0.4575, option.Some(2), option.Some(piecewise.RoundToZero))
|> should.equal(Ok(-0.45))
|> should.equal(-0.45)
piecewise.round(-0.5075, option.Some(2), option.Some(piecewise.RoundToZero))
|> should.equal(Ok(-0.5))
|> should.equal(-0.5)
}
pub fn math_round_ties_away_test() {
// Try with positive values
piecewise.round(1.4, option.Some(0), option.Some(piecewise.RoundTiesAway))
|> should.equal(Ok(1.0))
|> should.equal(1.0)
piecewise.round(1.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
|> should.equal(Ok(2.0))
|> should.equal(2.0)
piecewise.round(2.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
|> should.equal(Ok(3.0))
|> should.equal(3.0)
// Try with negative values
piecewise.round(-1.4, option.Some(0), option.Some(piecewise.RoundTiesAway))
|> should.equal(Ok(-1.0))
|> should.equal(-1.0)
piecewise.round(-1.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
|> should.equal(Ok(-2.0))
|> should.equal(-2.0)
piecewise.round(-2.0, option.Some(0), option.Some(piecewise.RoundTiesAway))
|> should.equal(Ok(-2.0))
|> should.equal(-2.0)
piecewise.round(-2.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
|> should.equal(Ok(-3.0))
|> should.equal(-3.0)
// Round 3. digit AFTER decimal point
piecewise.round(12.0654, option.Some(3), option.Some(piecewise.RoundTiesAway))
|> should.equal(Ok(12.065))
|> should.equal(12.065)
// Round 2. digit AFTER decimal point
piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundTiesAway))
|> should.equal(Ok(12.07))
|> should.equal(12.07)
// Round 1. digit AFTER decimal point
piecewise.round(12.0654, option.Some(1), option.Some(piecewise.RoundTiesAway))
|> should.equal(Ok(12.1))
|> should.equal(12.1)
// Round 0. digit BEFORE decimal point
piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundTiesAway))
|> should.equal(Ok(12.0))
|> should.equal(12.0)
// Round 1. digit BEFORE decimal point
piecewise.round(
@ -316,7 +316,7 @@ pub fn math_round_ties_away_test() {
option.Some(-1),
option.Some(piecewise.RoundTiesAway),
)
|> should.equal(Ok(10.0))
|> should.equal(10.0)
// Round 2. digit BEFORE decimal point
piecewise.round(
@ -324,7 +324,7 @@ pub fn math_round_ties_away_test() {
option.Some(-2),
option.Some(piecewise.RoundTiesAway),
)
|> should.equal(Ok(0.0))
|> should.equal(0.0)
// Round 2. digit BEFORE decimal point
piecewise.round(
@ -332,70 +332,70 @@ pub fn math_round_ties_away_test() {
option.Some(-3),
option.Some(piecewise.RoundTiesAway),
)
|> should.equal(Ok(0.0))
|> should.equal(0.0)
}
pub fn math_round_ties_up_test() {
// Try with positive values
piecewise.round(1.4, option.Some(0), option.Some(piecewise.RoundTiesUp))
|> should.equal(Ok(1.0))
|> should.equal(1.0)
piecewise.round(1.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
|> should.equal(Ok(2.0))
|> should.equal(2.0)
piecewise.round(2.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
|> should.equal(Ok(3.0))
|> should.equal(3.0)
// Try with negative values
piecewise.round(-1.4, option.Some(0), option.Some(piecewise.RoundTiesUp))
|> should.equal(Ok(-1.0))
|> should.equal(-1.0)
piecewise.round(-1.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
|> should.equal(Ok(-1.0))
|> should.equal(-1.0)
piecewise.round(-2.0, option.Some(0), option.Some(piecewise.RoundTiesUp))
|> should.equal(Ok(-2.0))
|> should.equal(-2.0)
piecewise.round(-2.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
|> should.equal(Ok(-2.0))
|> should.equal(-2.0)
// Round 3. digit AFTER decimal point
piecewise.round(12.0654, option.Some(3), option.Some(piecewise.RoundTiesUp))
|> should.equal(Ok(12.065))
|> should.equal(12.065)
// Round 2. digit AFTER decimal point
piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundTiesUp))
|> should.equal(Ok(12.07))
|> should.equal(12.07)
// Round 1. digit AFTER decimal point
piecewise.round(12.0654, option.Some(1), option.Some(piecewise.RoundTiesUp))
|> should.equal(Ok(12.1))
|> should.equal(12.1)
// Round 0. digit BEFORE decimal point
piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundTiesUp))
|> should.equal(Ok(12.0))
|> should.equal(12.0)
// Round 1. digit BEFORE decimal point
piecewise.round(12.0654, option.Some(-1), option.Some(piecewise.RoundTiesUp))
|> should.equal(Ok(10.0))
|> should.equal(10.0)
// Round 2. digit BEFORE decimal point
piecewise.round(12.0654, option.Some(-2), option.Some(piecewise.RoundTiesUp))
|> should.equal(Ok(0.0))
|> should.equal(0.0)
// Round 2. digit BEFORE decimal point
piecewise.round(12.0654, option.Some(-3), option.Some(piecewise.RoundTiesUp))
|> should.equal(Ok(0.0))
|> should.equal(0.0)
}
pub fn math_round_edge_cases_test() {
// The default number of digits is 0 if None is provided
piecewise.round(12.0654, option.None, option.Some(piecewise.RoundNearest))
|> should.equal(Ok(12.0))
|> should.equal(12.0)
// The default rounding mode is piecewise.RoundNearest if None is provided
piecewise.round(12.0654, option.None, option.None)
|> should.equal(Ok(12.0))
|> should.equal(12.0)
}
pub fn float_absolute_value_test() {