aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--src/gleam/float.gleam31
-rw-r--r--src/gleam/int.gleam13
-rw-r--r--src/gleam_stdlib.erl2
-rw-r--r--src/gleam_stdlib.mjs10
-rw-r--r--test/gleam/float_test.gleam33
-rw-r--r--test/gleam/int_test.gleam30
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() {