diff options
author | Nicklas Sindlev Andersen <nsa200293@live.dk> | 2022-06-07 19:55:57 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-07 18:55:57 +0100 |
commit | eeceba29e9d27fe1820bf2d806d4699a37a0469c (patch) | |
tree | d14003ab111183644e00f92b48465a379f757a5e | |
parent | 32d63fb1a0d8d46d17ef7077a4eb1e795dd1cc62 (diff) | |
download | gleam_stdlib-eeceba29e9d27fe1820bf2d806d4699a37a0469c.tar.gz gleam_stdlib-eeceba29e9d27fe1820bf2d806d4699a37a0469c.zip |
Fix int.power and float.power functions (#302)
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | src/gleam/float.gleam | 31 | ||||
-rw-r--r-- | src/gleam/int.gleam | 13 | ||||
-rw-r--r-- | src/gleam_stdlib.erl | 2 | ||||
-rw-r--r-- | src/gleam_stdlib.mjs | 10 | ||||
-rw-r--r-- | test/gleam/float_test.gleam | 33 | ||||
-rw-r--r-- | test/gleam/int_test.gleam | 30 |
7 files changed, 91 insertions, 29 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 972e422..106b4e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - The `string` module gains the `first`, `last`, and `capitalise` functions. - Fixed a bug where `string.reverse` would break utf8 strings on target JavaScript. - Fixed a bug where `string.slice` would break utf8 strings on target JavaScript. +- Fixed the `int.power` and `float.power` functions by properly handling error cases. - The grapheme iterator used by `string.graphemes` is now locale independent on target JavaScript. ## v0.21.0 - 2022-04-24 diff --git a/src/gleam/float.gleam b/src/gleam/float.gleam index 991030c..e72eb0d 100644 --- a/src/gleam/float.gleam +++ b/src/gleam/float.gleam @@ -257,21 +257,33 @@ pub fn absolute_value(x: Float) -> Float { /// /// ```gleam /// > power(2.0, -1.0) -/// 0.5 +/// Ok(0.5) /// -/// ```gleam /// > power(2.0, 2.0) -/// 4.0 +/// Ok(4.0) /// /// > power(8.0, 1.5) -/// 22.627416997969522 +/// Ok(22.627416997969522) /// /// > 4.0 |> power(of: 2.0) -/// 16.0 +/// Ok(16.0) +/// +/// > power(-1.0, 0.5) +/// Error(Nil) /// ``` /// -pub fn power(base: Float, of exponent: Float) -> Float { - do_power(base, exponent) +pub fn power(base: Float, of exponent: Float) -> Result(Float, Nil) { + let fractional: Bool = ceiling(exponent) -. exponent >. 0. + // In the following check: + // 1. If the base is negative and the exponent is fractional then + // return an error as it will otherwise be an imaginary number + // 2. If the base is 0 and the exponent is negative then the expression + // is equivalent to the exponent divided by 0 and an error should be + // returned + case base <. 0. && fractional || base == 0. && exponent <. 0. { + True -> Error(Nil) + False -> Ok(do_power(base, exponent)) + } } if erlang { @@ -297,10 +309,7 @@ if javascript { /// ``` /// pub fn square_root(x: Float) -> Result(Float, Nil) { - case x <. 0.0 { - True -> Error(Nil) - False -> Ok(power(x, 0.5)) - } + power(x, 0.5) } /// Returns the negative of the value provided. diff --git a/src/gleam/int.gleam b/src/gleam/int.gleam index 56ac399..d110c3f 100644 --- a/src/gleam/int.gleam +++ b/src/gleam/int.gleam @@ -27,19 +27,22 @@ pub fn absolute_value(x: Int) -> Int { /// /// ```gleam /// > power(2, -1.0) -/// 0.5 +/// Ok(0.5) /// /// > power(2, 2.0) -/// 4 +/// Ok(4.0) /// /// > power(8, 1.5) -/// 22.627416997969522 +/// Ok(22.627416997969522) /// /// > 4 |> power(of: 2.0) -/// 16.0 +/// Ok(16.0) +/// +/// > power(-1, 0.5) +/// Error(Nil) /// ``` /// -pub fn power(base: Int, of exponent: Float) -> Float { +pub fn power(base: Int, of exponent: Float) -> Result(Float, Nil) { base |> to_float() |> float.power(exponent) diff --git a/src/gleam_stdlib.erl b/src/gleam_stdlib.erl index 9dc769b..6fd44ed 100644 --- a/src/gleam_stdlib.erl +++ b/src/gleam_stdlib.erl @@ -374,4 +374,4 @@ inspect(Any) when is_function(Any) -> ), ["//fn(", Args, ") { ... }"]; inspect(Any) -> - ["//erl(", io_lib:format("~p", [Any]), ")"]. + ["//erl(", io_lib:format("~p", [Any]), ")"].
\ No newline at end of file diff --git a/src/gleam_stdlib.mjs b/src/gleam_stdlib.mjs index 97fa491..b4f3d22 100644 --- a/src/gleam_stdlib.mjs +++ b/src/gleam_stdlib.mjs @@ -210,7 +210,7 @@ export function bit_string_to_string(bit_string) { let decoder = new TextDecoder("utf-8", { fatal: true }); return new Ok(decoder.decode(bit_string.buffer)); } catch (_error) { - return new Error(undefined); + return new Error(Nil); } } @@ -239,7 +239,13 @@ export function truncate(float) { } export function power(base, exponent) { - return Math.pow(base, exponent); + // It is checked in Gleam that: + // - The base is non-negative and that the exponent is not fractional. + // - The base is not zero and the exponent is not negative (otherwise + // the result will essentially be divion by zero). + // It can thus be assumed that valid input is passed to the Math.pow + // function and a NaN or Infinity value will not be produced. + return Math.pow(base, exponent) } export function random_uniform() { diff --git a/test/gleam/float_test.gleam b/test/gleam/float_test.gleam index 132cd69..e952908 100644 --- a/test/gleam/float_test.gleam +++ b/test/gleam/float_test.gleam @@ -245,19 +245,42 @@ pub fn absolute_value_test() { pub fn power_test() { float.power(2.0, 2.0) - |> should.equal(4.0) + |> should.equal(Ok(4.0)) float.power(-5.0, 3.0) - |> should.equal(-125.0) + |> should.equal(Ok(-125.0)) float.power(10.5, 0.0) - |> should.equal(1.0) + |> should.equal(Ok(1.0)) float.power(16.0, 0.5) - |> should.equal(4.0) + |> should.equal(Ok(4.0)) float.power(2.0, -1.0) - |> should.equal(0.5) + |> should.equal(Ok(0.5)) + + float.power(2.0, -1.0) + |> should.equal(Ok(0.5)) + + // float.power(-1.0, 0.5) is equivalent to float.square_root(-1.0) + // and should return an error as an imaginary number would otherwise + // have to be returned + float.power(-1.0, 0.5) + |> should.equal(Error(Nil)) + + // Check another case with a negative base and fractional exponent + float.power(-1.5, 1.5) + |> should.equal(Error(Nil)) + + // float.power(0.0, -1.0) is equivalent to 1. /. 0 and is expected + // to be an error + float.power(0.0, -1.0) + |> should.equal(Error(Nil)) + + // Check that a negative base and exponent is fine as long as the + // exponent is not fractional + float.power(-2.0, -1.0) + |> should.equal(Ok(-0.5)) } pub fn square_root_test() { diff --git a/test/gleam/int_test.gleam b/test/gleam/int_test.gleam index 2209490..71c526b 100644 --- a/test/gleam/int_test.gleam +++ b/test/gleam/int_test.gleam @@ -257,19 +257,39 @@ pub fn is_odd_test() { pub fn power_test() { int.power(2, 2.0) - |> should.equal(4.0) + |> should.equal(Ok(4.0)) int.power(-5, 3.0) - |> should.equal(-125.0) + |> should.equal(Ok(-125.0)) int.power(10, 0.0) - |> should.equal(1.0) + |> should.equal(Ok(1.0)) int.power(16, 0.5) - |> should.equal(4.0) + |> should.equal(Ok(4.0)) int.power(2, -1.0) - |> should.equal(0.5) + |> should.equal(Ok(0.5)) + + // int.power(-1, 0.5) is equivalent to int.square_root(-1) and should + // return an error as an imaginary number would otherwise have to be + // returned + int.power(-1, 0.5) + |> should.equal(Error(Nil)) + + // Check another case with a negative base and fractional exponent + int.power(-1, 1.5) + |> should.equal(Error(Nil)) + + // float.power(0, -1) is equivalent to 1 / 0 and is expected + // to be an error + int.power(0, -1.0) + |> should.equal(Error(Nil)) + + // Check that a negative base and exponent is fine as long as the + // exponent is not fractional + int.power(-2, -1.0) + |> should.equal(Ok(-0.5)) } pub fn square_root_test() { |