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
last changed: 2024-03-07