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

f32.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 f32
#
#  Author: Fridtjof Siebert (siebert@tokiwa.software)
#
# -----------------------------------------------------------------------

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


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


  # comparison
  #
  type.equal (a, b f32) bool => intrinsic
  type.lower_than_or_equal (a, b f32) bool => intrinsic


  # does this `f32` value fit into an `i64`?  Used in inherited
  # precondition of `as_i64`.
  #
  public redef fits_in_i64 bool =>
    i64.min.as_f32 ≤ val ≤ i64.max.as_f32


  # conversion
  public redef as_i64 i64 => as_f64.as_i64


  public as_f64 f64 => intrinsic


  # casting bit representation to u32
  public cast_to_u32 u32 => 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 fixed redef type.hash_code(a f32) u64 =>
    if a.is_NaN
      hash NaN.cast_to_u32
    else if a = zero
      hash zero.cast_to_u32
    else
      hash a.cast_to_u32



  # is the sign bit set?
  public redef is_sign_bit_set bool =>
    cast_to_u32 >= 1P31


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


  # number of bits used for exponent
  #
  public redef type.exponent_bits i32 => 8


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


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


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


  # the biased exponent
  public redef exponent_biased i32 => ((cast_to_u32 >> f32.mantissa_bits.as_u32) & f32.exponent_mask).as_i32


  # the normalized exponent
  public redef exponent i32 =>
    if exponent_biased = 0
      1 - f32.exponent_bias
    else
      exponent_biased - f32.exponent_bias


  # the normalized mantissa
  public redef mantissa u64 =>
    m := cast_to_u32 & f32.mantissa_mask
    if exponent_biased = 0
      m.as_u64
    else
      ((u32 1 << f32.mantissa_bits.as_u32) | m).as_u64



  # the fraction of the floating point number
  public fixed redef fract f32 =>
    if val.is_NaN
      f32.NaN
    else if val < (f32 0)
      -(-val).fract
    else if val < (f32 1)
      val
    else
      shift := (f32.mantissa_bits - exponent)
      if (shift > 0)
        whole := cast_to_u32 & (u32.max << shift.as_u32)
        val - whole.cast_to_f32
      else
        0.0


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


  public fixed redef is_NaN bool => intrinsic

  public fixed redef square_root f32 => intrinsic

  public fixed redef exp f32 => intrinsic
  public fixed redef log f32 => intrinsic


  public fixed redef sin f32 => intrinsic
  public fixed redef cos f32 => intrinsic
  public fixed redef tan f32 => intrinsic
  public fixed redef asin f32 => intrinsic
  public fixed redef acos f32 => intrinsic
  public fixed redef atan f32 => intrinsic

  public fixed redef sinh f32 => intrinsic
  public fixed redef cosh f32 => intrinsic
  public fixed redef tanh f32 => intrinsic


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


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


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


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


  public fixed redef type.bytes i32 => 4


  public fixed redef type.ℇ f32 => f32 2.7182818284590452354


  public fixed redef type.π f32 => f32 3.14159265358979323846


  public fixed redef type.from_i64(val i64) f32 =>
    val.as_f32


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


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


  # equality
  #
  # This provides an equivalence relation in the mathematical
  # sense and therefore breaks the IEEE semantics. infix = should
  # be used for IEEE semantics.
  #
  # The equivalence relation is provided by the usual comparison
  # of floating-point numbers, with the definition of NaN = NaN.
  #
  public fixed redef type.equality(a, b f32) bool =>
    (f32.equal a b) || (a.is_NaN && b.is_NaN)


  # total order
  #
  # This provides a total order in the mathematical sense and
  # therefore breaks the IEEE semantics. infix <= should be
  # used for IEEE semantics.
  #
  # The total order is provided by the usual comparison of
  # floating-point numbers, with the definition that NaN <= x,
  # for any x (including x = NaN).
  #
  public fixed redef type.lteq(a, b f32) bool =>
    (f32.lower_than_or_equal a b) || a.is_NaN

last changed: 2025-06-17