//// Functions for working with floats. //// //// ## Float representation //// //// Floats are represented as 64 bit floating point numbers on both the Erlang //// and JavaScript runtimes. The floating point behaviour is native to their //// respective runtimes, so their exact behaviour will be slightly different on //// the two runtimes. //// //// ### Infinity and NaN //// //// Under the JavaScript runtime, exceeding the maximum (or minimum) //// representable value for a floating point value will result in Infinity (or //// -Infinity). Should you try to divide two infinities you will get NaN as a //// result. //// //// When running on BEAM, exceeding the maximum (or minimum) representable //// value for a floating point value will raise an error. //// //// ## Division by zero //// //// Gleam runs on the Erlang virtual machine, which does not follow the IEEE //// 754 standard for floating point arithmetic and does not have an `Infinity` //// value. In Erlang division by zero results in a crash, however Gleam does //// not have partial functions and operators in core so instead division by zero //// returns zero, a behaviour taken from Pony, Coq, and Lean. //// //// This may seem unexpected at first, but it is no less mathematically valid //// than crashing or returning a special value. Division by zero is undefined //// in mathematics. import gleam/order.{type Order} /// Attempts to parse a string as a `Float`, returning `Error(Nil)` if it was /// not possible. /// /// ## Examples /// /// ```gleam /// assert parse("2.3") == Ok(2.3) /// ``` /// /// ```gleam /// assert parse("ABC") == Error(Nil) /// ``` /// @external(erlang, "gleam_stdlib", "parse_float") @external(javascript, "../gleam_stdlib.mjs", "parse_float") pub fn parse(string: String) -> Result(Float, Nil) /// Returns the string representation of the provided `Float`. /// /// ## Examples /// /// ```gleam /// assert to_string(2.3) == "2.3" /// ``` /// @external(erlang, "gleam_stdlib", "float_to_string") @external(javascript, "../gleam_stdlib.mjs", "float_to_string") pub fn to_string(x: Float) -> String /// Restricts a float between two bounds. /// /// Note: If the `min` argument is larger than the `max` argument then they /// will be swapped, so the minimum bound is always lower than the maximum /// bound. /// /// /// ## Examples /// /// ```gleam /// assert clamp(1.2, min: 1.4, max: 1.6) == 1.4 /// ``` /// /// ```gleam /// assert clamp(1.2, min: 1.4, max: 0.6) == 1.2 /// ``` /// pub fn clamp(x: Float, min min_bound: Float, max max_bound: Float) -> Float { case min_bound >=. max_bound { True -> x |> min(min_bound) |> max(max_bound) False -> x |> min(max_bound) |> max(min_bound) } } /// Compares two `Float`s, returning an `Order`: /// `Lt` for lower than, `Eq` for equals, or `Gt` for greater than. /// /// ## Examples /// /// ```gleam /// assert compare(2.0, 2.3) == Lt /// ``` /// /// To handle /// [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems) /// you may use [`loosely_compare`](#loosely_compare) instead. /// pub fn compare(a: Float, with b: Float) -> Order { case a == b { True -> order.Eq False -> case a <. b { True -> order.Lt False -> order.Gt } } } /// Compares two `Float`s within a tolerance, returning an `Order`: /// `Lt` for lower than, `Eq` for equals, or `Gt` for greater than. /// /// This function allows Float comparison while handling /// [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems). /// /// Notice: For `Float`s the tolerance won't be exact: /// `5.3 - 5.0` is not exactly `0.3`. /// /// ## Examples /// /// ```gleam /// assert loosely_compare(5.0, with: 5.3, tolerating: 0.5) == Eq /// ``` /// /// If you want to check only for equality you may use /// [`loosely_equals`](#loosely_equals) instead. /// pub fn loosely_compare( a: Float, with b: Float, tolerating tolerance: Float, ) -> Order { let difference = absolute_value(a -. b) case difference <=. tolerance { True -> order.Eq False -> compare(a, b) } } /// Checks for equality of two `Float`s within a tolerance, /// returning a `Bool`. /// /// This function allows Float comparison while handling /// [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems). /// /// Notice: For `Float`s the tolerance won't be exact: /// `5.3 - 5.0` is not exactly `0.3`. /// /// ## Examples /// /// ```gleam /// assert loosely_equals(5.0, with: 5.3, tolerating: 0.5) /// ``` /// /// ```gleam /// assert !loosely_equals(5.0, with: 5.1, tolerating: 0.1) /// ``` /// pub fn loosely_equals( a: Float, with b: Float, tolerating tolerance: Float, ) -> Bool { let difference = absolute_value(a -. b) difference <=. tolerance } /// Compares two `Float`s, returning the smaller of the two. /// /// ## Examples /// /// ```gleam /// assert min(2.0, 2.3) == 2.0 /// ``` /// pub fn min(a: Float, b: Float) -> Float { case a <. b { True -> a False -> b } } /// Compares two `Float`s, returning the larger of the two. /// /// ## Examples /// /// ```gleam /// assert max(2.0, 2.3) == 2.3 /// ``` /// pub fn max(a: Float, b: Float) -> Float { case a >. b { True -> a False -> b } } /// Rounds the value to the next highest whole number as a `Float`. /// /// ## Examples /// /// ```gleam /// assert ceiling(2.3) == 3.0 /// ``` /// @external(erlang, "math", "ceil") @external(javascript, "../gleam_stdlib.mjs", "ceiling") pub fn ceiling(x: Float) -> Float /// Rounds the value to the next lowest whole number as a `Float`. /// /// ## Examples /// /// ```gleam /// assert floor(2.3) == 2.0 /// ``` /// @external(erlang, "math", "floor") @external(javascript, "../gleam_stdlib.mjs", "floor") pub fn floor(x: Float) -> Float /// Rounds the value to the nearest whole number as an `Int`. /// /// ## Examples /// /// ```gleam /// assert round(2.3) == 2 /// ``` /// /// ```gleam /// assert round(2.5) == 3 /// ``` /// @external(erlang, "erlang", "round") pub fn round(x: Float) -> Int { case x >=. 0.0 { True -> js_round(x) False -> 0 - js_round(negate(x)) } } @external(javascript, "../gleam_stdlib.mjs", "round") fn js_round(a: Float) -> Int /// Returns the value as an `Int`, truncating all decimal digits. /// /// ## Examples /// /// ```gleam /// assert truncate(2.4343434847383438) == 2 /// ``` /// @external(erlang, "erlang", "trunc") @external(javascript, "../gleam_stdlib.mjs", "truncate") pub fn truncate(x: Float) -> Int /// Converts the value to a given precision as a `Float`. /// The precision is the number of allowed decimal places. /// Negative precisions are allowed and force rounding /// to the nearest tenth, hundredth, thousandth etc. /// /// ## Examples /// /// ```gleam /// assert to_precision(2.43434348473, 2) == 2.43 /// ``` /// /// ```gleam /// assert to_precision(547890.453444, -3) == 548000.0 /// ``` /// pub fn to_precision(x: Float, precision: Int) -> Float { case precision <= 0 { True -> { let factor = do_power(10.0, do_to_float(-precision)) do_to_float(round(x /. factor)) *. factor } False -> { let factor = do_power(10.0, do_to_float(precision)) do_to_float(round(x *. factor)) /. factor } } } @external(erlang, "erlang", "float") @external(javascript, "../gleam_stdlib.mjs", "identity") fn do_to_float(a: Int) -> Float /// Returns the absolute value of the input as a `Float`. /// /// ## Examples /// /// ```gleam /// assert absolute_value(-12.5) == 12.5 /// ``` /// /// ```gleam /// assert absolute_value(10.2) == 10.2 /// ``` /// pub fn absolute_value(x: Float) -> Float { case x >=. 0.0 { True -> x False -> 0.0 -. x } } /// Returns the result of the base being raised to the power of the /// exponent, as a `Float`. /// /// ## Examples /// /// ```gleam /// assert power(2.0, -1.0) == Ok(0.5) /// ``` /// /// ```gleam /// assert power(2.0, 2.0) == Ok(4.0) /// ``` /// /// ```gleam /// assert power(8.0, 1.5) == Ok(22.627416997969522) /// ``` /// /// ```gleam /// assert 4.0 |> power(of: 2.0) == Ok(16.0) /// ``` /// /// ```gleam /// assert power(-1.0, 0.5) == Error(Nil) /// ``` /// pub fn power(base: Float, of exponent: Float) -> Result(Float, Nil) { let fractional: Bool = ceiling(exponent) -. exponent >. 0.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.0 && fractional || base == 0.0 && exponent <. 0.0 { True -> Error(Nil) False -> Ok(do_power(base, exponent)) } } @external(erlang, "math", "pow") @external(javascript, "../gleam_stdlib.mjs", "power") fn do_power(a: Float, b: Float) -> Float /// Returns the square root of the input as a `Float`. /// /// ## Examples /// /// ```gleam /// assert square_root(4.0) == Ok(2.0) /// ``` /// /// ```gleam /// assert square_root(-16.0) == Error(Nil) /// ``` /// pub fn square_root(x: Float) -> Result(Float, Nil) { power(x, 0.5) } /// Returns the negative of the value provided. /// /// ## Examples /// /// ```gleam /// assert negate(1.0) == -1.0 /// ``` /// pub fn negate(x: Float) -> Float { -1.0 *. x } /// Sums a list of `Float`s. /// /// ## Example /// /// ```gleam /// assert sum([1.0, 2.2, 3.3]) == 6.5 /// ``` /// pub fn sum(numbers: List(Float)) -> Float { sum_loop(numbers, 0.0) } fn sum_loop(numbers: List(Float), initial: Float) -> Float { case numbers { [first, ..rest] -> sum_loop(rest, first +. initial) [] -> initial } } /// Multiplies a list of `Float`s and returns the product. /// /// ## Example /// /// ```gleam /// assert product([2.5, 3.2, 4.2]) == 33.6 /// ``` /// pub fn product(numbers: List(Float)) -> Float { product_loop(numbers, 1.0) } fn product_loop(numbers: List(Float), initial: Float) -> Float { case numbers { [first, ..rest] -> product_loop(rest, first *. initial) [] -> initial } } /// Generates a random float between the given zero (inclusive) and one /// (exclusive). /// /// On Erlang this updates the random state in the process dictionary. /// See: /// /// ## Examples /// /// ```gleam /// random() /// // -> 0.646355926896028 /// ``` /// @external(erlang, "rand", "uniform") @external(javascript, "../gleam_stdlib.mjs", "random_uniform") pub fn random() -> Float /// Computes the modulo of a float division of inputs as a `Result`. /// /// Returns division of the inputs as a `Result`: If the given divisor equals /// `0`, this function returns an `Error`. /// /// The computed value will always have the same sign as the `divisor`. /// /// ## Examples /// /// ```gleam /// assert modulo(13.3, by: 3.3) == Ok(0.1) /// ``` /// /// ```gleam /// assert modulo(-13.3, by: 3.3) == Ok(3.2) /// ``` /// /// ```gleam /// assert modulo(13.3, by: -3.3) == Ok(-3.2) /// ``` /// /// ```gleam /// assert modulo(-13.3, by: -3.3) == Ok(-0.1) /// ``` /// pub fn modulo(dividend: Float, by divisor: Float) -> Result(Float, Nil) { case divisor { 0.0 -> Error(Nil) _ -> Ok(dividend -. floor(dividend /. divisor) *. divisor) } } /// Returns division of the inputs as a `Result`. /// /// ## Examples /// /// ```gleam /// assert divide(0.0, 1.0) == Ok(0.0) /// ``` /// /// ```gleam /// assert divide(1.0, 0.0) == Error(Nil) /// ``` /// pub fn divide(a: Float, by b: Float) -> Result(Float, Nil) { case b { 0.0 -> Error(Nil) b -> Ok(a /. b) } } /// Adds two floats together. /// /// It's the function equivalent of the `+.` operator. /// This function is useful in higher order functions or pipes. /// /// ## Examples /// /// ```gleam /// assert add(1.0, 2.0) == 3.0 /// ``` /// /// ```gleam /// import gleam/list /// /// assert list.fold([1.0, 2.0, 3.0], 0.0, add) == 6.0 /// ``` /// /// ```gleam /// assert 3.0 |> add(2.0) == 5.0 /// ``` /// pub fn add(a: Float, b: Float) -> Float { a +. b } /// Multiplies two floats together. /// /// It's the function equivalent of the `*.` operator. /// This function is useful in higher order functions or pipes. /// /// ## Examples /// /// ```gleam /// assert multiply(2.0, 4.0) == 8.0 /// ``` /// /// ```gleam /// import gleam/list /// /// assert list.fold([2.0, 3.0, 4.0], 1.0, multiply) == 24.0 /// ``` /// /// ```gleam /// assert 3.0 |> multiply(2.0) == 6.0 /// ``` /// pub fn multiply(a: Float, b: Float) -> Float { a *. b } /// Subtracts one float from another. /// /// It's the function equivalent of the `-.` operator. /// This function is useful in higher order functions or pipes. /// /// ## Examples /// /// ```gleam /// assert subtract(3.0, 1.0) == 2.0 /// ``` /// /// ```gleam /// import gleam/list /// /// assert list.fold([1.0, 2.0, 3.0], 10.0, subtract) == 4.0 /// ``` /// /// ```gleam /// assert 3.0 |> subtract(_, 2.0) == 1.0 /// ``` /// /// ```gleam /// assert 3.0 |> subtract(2.0, _) == -1.0 /// ``` /// pub fn subtract(a: Float, b: Float) -> Float { a -. b } /// Returns the natural logarithm (base e) of the given `Float` as a `Result`. If the /// input is less than or equal to 0, returns `Error(Nil)`. /// /// ## Examples /// /// ```gleam /// assert logarithm(1.0) == Ok(0.0) /// ``` /// /// ```gleam /// assert logarithm(2.718281828459045) == Ok(1.0) /// ``` /// /// ```gleam /// assert logarithm(0.0) == Error(Nil) /// ``` /// /// ```gleam /// assert logarithm(-1.0) == Error(Nil) /// ``` /// pub fn logarithm(x: Float) -> Result(Float, Nil) { // In the following check: // 1. If x is negative then return an error as the natural logarithm // of a negative number is undefined (would be a complex number) // 2. If x is 0 then return an error as the natural logarithm of 0 // approaches negative infinity case x <=. 0.0 { True -> Error(Nil) False -> Ok(do_log(x)) } } @external(erlang, "math", "log") @external(javascript, "../gleam_stdlib.mjs", "log") fn do_log(x: Float) -> Float /// Returns e (Euler's number) raised to the power of the given exponent, as /// a `Float`. /// /// ## Examples /// /// ```gleam /// assert exponential(0.0) == Ok(1.0) /// ``` /// /// ```gleam /// assert exponential(1.0) == Ok(2.718281828459045) /// ``` /// /// ```gleam /// assert exponential(-1.0) == Ok(0.36787944117144233) /// ``` /// @external(erlang, "math", "exp") @external(javascript, "../gleam_stdlib.mjs", "exp") pub fn exponential(x: Float) -> Float