Fuzion Logo
fuzion-lang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

f64.fz


# This file is part of the Fuzion language implementation.
#
# The Fuzion language implementation is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, version 3 of the License.
#
# The Fuzion language implementation is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with The
# Fuzion language implementation.  If not, see <https://www.gnu.org/licenses/>.


# -----------------------------------------------------------------------
#
#  Tokiwa Software GmbH, Germany
#
#  Source code of Fuzion standard library feature f64
#
#  Author: Fridtjof Siebert (siebert@tokiwa.software)
#
# -----------------------------------------------------------------------

# f64 -- 64 bit floating point values
#
#
# f64 are binary64-numbers as defined in the IEEE 754-2019 standard, see
# https://ieeexplore.ieee.org/servlet/opac?punumber=8766227
#
public f64(public val f64) : float is

  public thiz => f64.this.val


  # basic operations: 'prefix -' (negation)
  public fixed redef prefix - f64 => intrinsic
  public fixed infix + (other f64) f64 => intrinsic
  public fixed infix - (other f64) f64 => intrinsic
  public fixed infix * (other f64) f64 => intrinsic
  public fixed infix / (other f64) f64 => intrinsic
  public fixed infix % (other f64) f64 => intrinsic
  public fixed infix ** (other f64) f64 => intrinsic


  # comparision
  #
  fixed infix = (other f64) bool => intrinsic_constructor
  fixed infix <= (other f64) bool => intrinsic_constructor
  fixed infix >= (other f64) bool => intrinsic_constructor
  fixed infix < (other f64) bool => intrinsic_constructor
  fixed infix > (other f64) bool => intrinsic_constructor


  # conversion

  # NaN is converted to 0
  # anything greater than i64.max as well as  ∞ is i64.max
  # anything lower   than i64.min as well as -∞ is i64.min
  public as_i64_lax i64 => intrinsic


  public fits_in_i64 =>
    (thiz ≥ i64.min.as_f64) && (thiz ≤ i64.max.as_f64)


  public redef as_i64
  pre
    safety: fits_in_i64
  => as_i64_lax


  public as_f32 f32 => intrinsic


  # casting bit representation to u64
  public cast_to_u64 u64 => intrinsic


  # create hash code from this number
  #
  # special handling for floats:
  # although -0.0 and 0.0 are different in bit representation,
  # they are considered equal by both type.equality and IEEE
  # standard, hence they should have the same hash.
  # all NaNs are considered equal by type.equality (but not
  # the IEEE standard), so the hash of any NaN is the hash of
  # the "canonical" NaN.
  #
  public type.hash_code(a f64.this) u64 =>
    if is_NaN a.val
      hash NaN.cast_to_u64
    else if a = zero
      hash zero.cast_to_u64
    else
      hash a.cast_to_u64


  # is the sign bit set?
  public is_sign_bit_set bool =>
    cast_to_u64 >= 1P63


  # number of bits used for mantissa,
  # including leading '1' that is not actually stored
  #
  public type.significand_bits => 53

  # number of bits used for exponent
  #
  public type.exponent_bits => 11


  # mask for the the bits that encode the mantissa
  public type.mantissa_mask => u64 1P52 - 1


  # mask for the the bits that encode the exponent
  # (the mask is not shifted to the correct position)
  public type.exponent_mask => u64 1P11 - 1


  # the exponent bias (the zero offset of the exponent)
  public type.exponent_bias => 1023


  # the biased exponent
  public exponent_biased => ((cast_to_u64 >> f64.mantissa_bits.as_u64) & f64.exponent_mask).as_i32


  # the normalized exponent
  public exponent =>
    if exponent_biased = 0
      1 - f64.exponent_bias
    else
      exponent_biased - f64.exponent_bias


  # the normalized mantissa
  public mantissa =>
    m := cast_to_u64 & f64.mantissa_mask
    if exponent_biased = 0
      m
    else
      (u64 1 << f64.mantissa_bits.as_u64) | m


  # the fraction of this floating point number
  public fixed redef fract f64 =>
    if f64.is_NaN val
      f64.NaN
    else if val < (f64 0)
      -(-val).fract
    else if val < (f64 1)
      val
    else
      shift := (f64.mantissa_bits - exponent)
      if (shift > 0)
        whole := cast_to_u64 & (u64.max << shift.as_u64)
        val - whole.cast_to_f64
      else
        0.0


  # true when the absolute value
  # is smaller than 0.001
  # or greater than 9_999_999
  #
  public use_scientific_notation bool =>
    abs<1E-3 || abs>=1E7


  # convert this to a string.
  #
  public redef as_string =>
    (num.ryū f64).as_string val


  # -----------------------------------------------------------------------
  #
  # type features:


  # identity element for 'infix +'
  #
  public fixed type.zero f64 => 0


  # identity element for 'infix *'
  #
  public fixed type.one  f64 => 1


  public fixed type.bytes => 8


  public fixed type.ℇ => 2.7182818284590452354


  public fixed type.π => 3.14159265358979323846


  public fixed type.from_i64(val i64) f64 =>
    val.as_f64


  public fixed type.is_NaN(val f64) bool => intrinsic_constructor


  public fixed type.min_exp i32 => intrinsic
  public fixed type.max_exp i32 => intrinsic
  public fixed type.min_positive f64 => intrinsic
  public fixed type.max f64 => intrinsic
  public fixed type.epsilon f64 => intrinsic


  public fixed type.square_root(val f64) f64 => intrinsic

  public fixed type.exp(val f64) f64 => intrinsic
  public fixed type.log(val f64) f64 => intrinsic


  public fixed type.sin(val f64) f64 => intrinsic
  public fixed type.cos(val f64) f64 => intrinsic
  public fixed type.tan(val f64) f64 => intrinsic
  public fixed type.asin(val f64) f64 => intrinsic
  public fixed type.acos(val f64) f64 => intrinsic
  public fixed type.atan(val f64) f64 => intrinsic

  # atan(y/x) with a few special cases
  # see also: https://go.dev/src/math/atan2.go
  #
  public fixed type.atan2(y, x f64) f64 =>
    if is_NaN y || is_NaN x
      NaN
    if y = 0
      if x > 0 || (x = 0 && x.is_sign_bit_set)
        y
      else
        if y.is_sign_bit_set then -π else π
    else if x = 0
      if y > 0 then π/2 else -π/2
    else if x = f64.positive_infinity
      if y = f64.positive_infinity
        π/4
      else if y = f64.negative_infinity
        -π/4
      else
        0
    else if x = f64.negative_infinity
      if y = f64.positive_infinity
        3.0*π/4
      else if y = f64.negative_infinity
        -3.0*π/4
      else if y > 0
        π
      else // y < 0
        -π
    else if y = f64.positive_infinity
      π/2
    else if y = f64.negative_infinity
      -π/2
    else
      q := atan y/x
      if x < 0
        if q <= 0 then q+π else q-π
      else
        q


  public fixed type.sinh(val f64) f64 => intrinsic
  public fixed type.cosh(val f64) f64 => intrinsic
  public fixed type.tanh(val f64) f64 => intrinsic