flix

0.77.0

Float64.flix

/*
 * Copyright 2016 Liam Palmer
 *
 * Use of this source code is governed by the Apache 2.0 license
 * that can be found in the LICENSE.md file.
 */

instance LowerBound[Float64] {
    pub def minValue(): Float64 = Float64.minValue()
}

instance UpperBound[Float64] {
    pub def maxValue(): Float64 = Float64.maxValue()
}

pub mod Float64 {

    import java.lang.Double
    import java.lang.Math
    import java.math.BigDecimal
    import java.lang.NumberFormatException

    ///
    /// Returns the number of bits used to represent a `Float64`.
    ///
    pub def size(): Int32 = 64

    ///
    /// Returns the maximum exponent that a `Float64` may have.
    ///
    pub def maxExponent(): Int32 = 1023

    ///
    /// Returns the minimum exponent that a `Float64` may have.
    ///
    pub def minExponent(): Int32 = -1022

    ///
    /// Returns the maximum number representable by a `Float64`.
    ///
    pub def maxValue(): Float64 = (2.0f64 - pow(base = 2.0f64, -52.0f64)) * pow(base = 2.0f64, 1023.0f64)

    ///
    /// Returns the minimum number representable by a `Float64`.
    ///
    pub def minValue(): Float64 = -maxValue()

    ///
    /// Returns the minimum positive number representable by a `Float64`.
    ///
    pub def minPositiveValue(): Float64 = pow(base = 2.0f64, -1074.0f64)

    ///
    /// Returns the NaN (not a number) value of type `Float64`.
    ///
    pub def nan(): Float64 = 0.0f64 / 0.0f64

    ///
    /// Returns the positive infinity value of type `Float64`.
    ///
    pub def positiveInfinity(): Float64 = 1.0f64 / 0.0f64

    ///
    /// Returns the negative infinity value of type `Float64`.
    ///
    pub def negativeInfinity(): Float64 = -1.0f64 / 0.0f64

    ///
    /// Returns true if and only if `x` is a non-infinite and non-Nan `Float64` value.
    ///
    pub def isFinite(x: Float64): Bool = x >= minValue() and x <= maxValue()

    ///
    /// Returns true if and only if `x` is an infinite and non-Nan `Float64` value.
    ///
    pub def isInfinite(x: Float64): Bool = x == positiveInfinity() or x == negativeInfinity()

    ///
    /// Returns true if and only if `x` is the NaN value of type `Float64`.
    ///
    pub def isNan(x: Float64): Bool = x != x

    ///
    /// Returns the smaller of `x` and `y`.
    ///
    pub def min(x: Float64, y: Float64): Float64 = if (x <= y) x else y

    ///
    /// Returns the larger of `x` and `y`.
    ///
    pub def max(x: Float64, y: Float64): Float64 = if (x >= y) x else y

    ///
    /// Return a string representation of `x`.
    ///
    pub def toString(x: Float64): String = ToString.toString(x)

    ///
    /// Parse the string `s` as a Float64, leading or trailing whitespace is trimmed.
    /// A successful parse is wrapped with `Some(x)`, a parse failure is indicated by `None`.
    ///
    pub def fromString(s: String): Option[Float64] = try {
        Some(Double.parseDouble(s.strip()))
    } catch {
        case _: NumberFormatException => None
    }

    ///
    /// Convert `x` to an `Option[Int8]`.
    ///
    /// Returns `Some(x as Int8)` if the numeric value of `x` is within the range
    /// of Int8, rounding `x` towards 0`.
    ///
    /// Returns `None` if the numeric value of `x` is outside the range of Int8
    /// (i.e. -128 to 127), or it is NaN or infinity.
    ///
    pub def tryToInt8(x: Float64): Option[Int8] =
        if (x < Int8.toFloat64(Int8.minValue()) or x > Int8.toFloat64(Int8.maxValue()) or isNan(x) or isInfinite(x))
            None
        else
            Some(Double.valueOf(x).byteValue())

    ///
    /// Convert `x` to an `Option[Int16]`.
    ///
    /// Returns `Some(x as Int16)` if the numeric value of `x` is within the range
    /// of Int16, rounding `x` towards 0`.
    ///
    /// Returns `None` if the numeric value of `x` is outside the range of Int16
    /// (i.e. -32768 to 32767), or it is NaN or infinity.
    ///
    pub def tryToInt16(x: Float64): Option[Int16] =
        if (x < Int16.toFloat64(Int16.minValue()) or x > Int16.toFloat64(Int16.maxValue()) or isNan(x) or isInfinite(x))
            None
        else
            Some(Double.valueOf(x).shortValue())

    ///
    /// Convert `x` to an `Option[Int32]`.
    ///
    /// Returns `Some(x as Int32)` if the numeric value of `x` is within the range
    /// of Int32, rounding `x` towards 0`.
    ///
    /// Returns `None` if the numeric value of `x` is outside the range of Int32
    /// (i.e. -2147483648 to 2147483647), or it is NaN or infinity.
    ///
    pub def tryToInt32(x: Float64): Option[Int32] =
        if (x < Int32.toFloat64(Int32.minValue()) or x > Int32.toFloat64(Int32.maxValue()) or isNan(x) or isInfinite(x))
            None
        else
            Some(Double.valueOf(x).intValue())

    ///
    /// Convert `x` to an `Option[Int64]`.
    ///
    /// Returns `Some(x as Int64)` if the numeric value of `x` is within the range
    /// of Int64, rounding `x` towards 0`.
    ///
    /// Returns `None` if the numeric value of `x` is outside the range of Int64
    /// (i.e. -9223372036854775808 to 9223372036854775807), or it is NaN or infinity.
    ///
    /// Note: while the range of an Int64 is precisely defined using Int64 values,
    /// converting this range to Float64 values is imprecise.
    ///
    pub def tryToInt64(x: Float64): Option[Int64] =
        if (x < Int64.toFloat64(Int64.minValue()) or x > Int64.toFloat64(Int64.maxValue()) or isNan(x) or isInfinite(x))
            None
        else
            Some(Double.valueOf(x).longValue())

    ///
    /// Convert `x` to an `Option[BigInt]`.
    ///
    /// Returns `Some(x as BigInt)` if the numeric value of `x` is representable as a BigInt.
    ///
    /// Returns `None` if the value of `x` is NaN or infinity.
    ///
    pub def tryToBigInt(x: Float64): Option[BigInt] =
        try {
            Some(BigDecimal.valueOf(x).toBigInteger())
        } catch {
            // Catch NaN, isInfinite.
            case _: NumberFormatException => None
        }

    ///
    /// Convert `x` to an `Option[Float32]`.
    ///
    /// Returns `Some(x as Float32)` if the numeric value of `x` is within the range
    /// of Float32, loss of precision may occur.
    ///
    /// Returns `None` if the numeric value of `x` is outside the range of Float32
    /// (i.e. 1.4E-45 to 3.4028235E38).
    ///
    /// If `x` is NaN return `Some(Float32.NaN)``, if `x` is positive or negative infinity return
    /// `Some` wrapping the corresponding Float32 infinity.
    ///
    pub def tryToFloat32(x: Float64): Option[Float32] =
        if (x < Float32.toFloat64(Float32.minValue()) or x > Float32.toFloat64(Float32.maxValue()))
            None
        else
            Some(Double.valueOf(x).floatValue())

    ///
    /// Convert `x` to an `Option[BigDecimal]`.
    ///
    /// Returns `Some(x as BigDecimal)` if the numeric value of `x` is representable
    /// as a BigDecimal value.
    ///
    /// If `x` is NaN or infinity return `None`.
    ///
    pub def tryToBigDecimal(x: Float64): Option[BigDecimal] =
        try {
            Some(new BigDecimal(x))
        } catch {
            case _: NumberFormatException => None
        }

    ///
    /// Helper function for the `clamp` conversion functions.
    ///
    def clamp(min: {min = Float64}, max: {max = Float64}, x: Float64): Float64 =
        if (x < min#min)
            min#min
        else if (x > max#max)
            max#max
        else
            x

    ///
    /// Convert `x` to an `Int8`.
    ///
    /// Returns `x` clamped within the Int8 range `min` to `max`.
    ///
    /// Warning: it is recommended to test `x` for NaN (not-a-number) before calling this
    /// function. Relying on `nanValue` to convert NaN to a permissable Int8 risks masking it.
    ///
    pub def clampToInt8(min: {min = Int8}, max: {max = Int8}, nanValue: {nanValue = Int8}, x: Float64): Int8 =
        let minf64 = Int8.toFloat64(min#min);
        let maxf64 = Int8.toFloat64(max#max);
        if (isNan(x))
            nanValue#nanValue
        else
            Double.valueOf(clamp(min = minf64, max = maxf64, x)).byteValue()

    ///
    /// Convert `x` to an `Int16`.
    ///
    /// Returns `x` clamped within the Int16 range `min` to `max`.
    ///
    /// Warning: it is recommended to test `x` for NaN (not-a-number) before calling this
    /// function. Relying on `nanValue` to convert NaN to a permissable Int16 risks masking it.
    ///
    pub def clampToInt16(min: {min = Int16}, max: {max = Int16}, nanValue: {nanValue = Int16}, x: Float64): Int16 =
        let minf64 = Int16.toFloat64(min#min);
        let maxf64 = Int16.toFloat64(max#max);
        if (isNan(x))
            nanValue#nanValue
        else
            Double.valueOf(clamp(min = minf64, max = maxf64, x)).shortValue()

    ///
    /// Convert `x` to an `Int32`.
    ///
    /// Returns `x` clamped within the Int32 range `min` to `max`.
    ///
    /// Warning: it is recommended to test `x` for NaN (not-a-number) before calling this
    /// function. Relying on `nanValue` to convert NaN to a permissable Int32 risks masking it.
    ///
    pub def clampToInt32(min: {min = Int32}, max: {max = Int32}, nanValue: {nanValue = Int32}, x: Float64): Int32 =
        let minf64 = Int32.toFloat64(min#min);
        let maxf64 = Int32.toFloat64(max#max);
        if (isNan(x))
            nanValue#nanValue
        else
            Double.valueOf(clamp(min = minf64, max = maxf64, x)).intValue()

    ///
    /// Convert `x` to an `Int64`.
    ///
    /// Returns `x` clamped within the Int64 range `min` to `max`.
    ///
    /// Warning: it is recommended to test `x` for NaN (not-a-number) before calling this
    /// function. Relying on `nanValue` to convert NaN to a permissable Int64 risks masking it.
    ///
    pub def clampToInt64(min: {min = Int64}, max: {max = Int64}, nanValue: {nanValue = Int64}, x: Float64): Int64 =
        let minf64 = Int64.toFloat64(min#min);
        let maxf64 = Int64.toFloat64(max#max);
        if (isNan(x))
            nanValue#nanValue
        else
            Double.valueOf(clamp(min = minf64, max = maxf64, x)).longValue()

    ///
    /// Convert `x` to a `Float32`.
    ///
    /// Returns `x` clamped within the Float32 range `min` to `max`.
    ///
    pub def clampToFloat32(min: {min = Float32}, max: {max = Float32}, x: Float64): Float32 =
        let minf64 = Float32.toFloat64(min#min);
        let maxf64 = Float32.toFloat64(max#max);
        if (isNan(x))
            Float32.nan()
        else
            Double.valueOf(clamp(min = minf64, max = maxf64, x)).floatValue()

    ///
    /// Convert `x` to an Int32 by truncation towards zero.
    ///
    /// Returns 0 if `x` is NaN. Saturates to `Int32.minValue()` or `Int32.maxValue()`
    /// if `x` is out of range or infinite.
    ///
    pub def truncateToInt32(x: Float64): Int32 =
        Double.valueOf(x).intValue()

    ///
    /// Convert `x` to an Int64 by truncation towards zero.
    ///
    /// Returns 0 if `x` is NaN. Saturates to `Int64.minValue()` or `Int64.maxValue()`
    /// if `x` is out of range or infinite.
    ///
    pub def truncateToInt64(x: Float64): Int64 =
        Double.valueOf(x).longValue()

    ///
    /// Convert `x` to a Float32.
    ///
    /// The numeric value of `x` may lose precision.
    ///
    pub def truncateToFloat32(x: Float64): Float32 =
        Double.valueOf(x).floatValue()

    ///
    /// Returns the absolute value of `x`.
    ///
    pub def abs(x: Float64): Float64 =
        Math.abs(x)

    ///
    /// Returns `x` rounded up to a Float64 representing the nearest larger integer value.
    ///
    pub def ceil(x: Float64): Float64 =
        Math.ceil(x)

    ///
    /// Returns `x` rounded down to a Float64 representing the nearest smaller integer value.
    ///
    pub def floor(x: Float64): Float64 =
        Math.floor(x)

    ///
    /// Returns `x` rounded to a Float64 representing the nearest integer value.
    ///
    /// The rounding may be upwards or downwards. If the rounding up and rounding down are equally
    /// close, `x` will be rounded to an even value (i.e. `round(0.5f64) == 0.0f64`).
    ///
    pub def round(x: Float64): Float64 =
        Math.rint(x)

    ///
    /// Returns ` base` raised to the power of `n`.
    ///
    pub def pow(base: {base = Float64}, n: Float64): Float64 = %%FLOAT64_EXP%%(base#base, n)

    ///
    /// Returns the exponential of `x`.
    ///
    pub def exp(x: Float64): Float64 =
        Math.exp(x)

    ///
    /// Get the primitive Float64 value from its object representation (i.e. java.lang.Double).
    ///
    /// This function is expected to be used when marshaling Float64s from Java. Generally in Flix
    /// code you should not need to use `java.lang.Double`.
    ///
    pub def doubleValue(i: Double): Float64 =
        i.doubleValue()

    ///
    /// Convert an Float64 value to its object representation (i.e. java.lang.Double).
    ///
    /// This function is expected to be used when marshaling Float64s to Java. Generally in Flix
    /// code you should not need to use `java.lang.Double`.
    ///
    pub def valueOf(i: Float64): Double =
        Double.valueOf(i)

}