Fuzion Logo
flang.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 is intrinsic
  public fixed infix + (other f64) f64 is intrinsic
  public fixed infix - (other f64) f64 is intrinsic
  public fixed infix * (other f64) f64 is intrinsic
  public fixed infix / (other f64) f64 is intrinsic
  public fixed infix % (other f64) f64 is intrinsic
  public fixed infix ** (other f64) f64 is intrinsic


  # comparision
  #
  fixed infix = (other f64) bool is intrinsic_constructor
  fixed infix <= (other f64) bool is intrinsic_constructor
  fixed infix >= (other f64) bool is intrinsic_constructor
  fixed infix < (other f64) bool is intrinsic_constructor
  fixed infix > (other f64) bool is 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 is intrinsic


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


  public redef as_i64
  pre
    safety: fits_in_i64
  => as_i64_lax


  public as_f32 f32 is intrinsic


  # casting bit representation to u64
  public cast_to_u64 u64 is 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 is
    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?
  module is_sign_bit_set bool is
    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.type.mantissa_bits.as_u64) & f64.type.exponent_mask).as_i32


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


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


  # the fraction of this floating point number
  public fixed redef fract f64 is
    if f64.type.is_NaN val
      f64.type.NaN
    else if val < (f64 0)
      -(-val).fract
    else if val < (f64 1)
      val
    else
      shift := (f64.type.mantissa_bits - exponent)
      if (shift > 0)
        whole := cast_to_u64 & (u64.type.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 is
    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 is 0


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


  public fixed type.bytes => 8


  public fixed type.ℇ => 2.7182818284590452354


  public fixed type.π => 3.14159265358979323846


  public fixed type.from_i64(val i64) f64 is
    val.as_f64


  public fixed type.is_NaN(val f64) bool is intrinsic_constructor


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


  public fixed type.square_root(val f64) f64 is intrinsic

  public fixed type.exp(val f64) f64 is intrinsic
  public fixed type.log(val f64) f64 is intrinsic


  public fixed type.sin(val f64) f64 is intrinsic
  public fixed type.cos(val f64) f64 is intrinsic
  public fixed type.tan(val f64) f64 is intrinsic
  public fixed type.asin(val f64) f64 is intrinsic
  public fixed type.acos(val f64) f64 is intrinsic
  public fixed type.atan(val f64) f64 is 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 is
    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.type.positive_infinity
      if y = f64.type.positive_infinity
        π/4
      else if y = f64.type.negative_infinity
        -π/4
      else
        0
    else if x = f64.type.negative_infinity
      if y = f64.type.positive_infinity
        3.0*π/4
      else if y = f64.type.negative_infinity
        -3.0*π/4
      else if y > 0
        π
      else // y < 0
        -π
    else if y = f64.type.positive_infinity
      π/2
    else if y = f64.type.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 is intrinsic
  public fixed type.cosh(val f64) f64 is intrinsic
  public fixed type.tanh(val f64) f64 is intrinsic