hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit d922eafdc52004801a96546555ad9286d5ea1b97
parent 468d2371b9d317706bb946c4acbd0c7c2ee59268
Author: Sebastian <sebastian@sebsite.pw>
Date:   Sun,  1 Dec 2024 20:25:28 -0500

math: remove generic float functions

f32 can promote to f64, and the generic functions always returned f64
(rather than f32) anyways. The only time the generic functions are
useful is when you have a types::floating value which you don't know the
actual type of. In that case, you now have to match on it yourself. I
think this is a worthwhile tradeoff for a cleaner API.

This is a breaking change.

Signed-off-by: Sebastian <sebastian@sebsite.pw>

Diffstat:
Mmath/+test/floats_test.ha | 90+++++++++++++++++++++++++++++++++----------------------------------------------
Mmath/+test/math_test.ha | 8++++----
Mmath/complex/complex.ha | 14+++++++-------
Mmath/floats.ha | 87+------------------------------------------------------------------------------
Mmath/math.ha | 32+++++---------------------------
Mmath/trig.ha | 2+-
6 files changed, 55 insertions(+), 178 deletions(-)

diff --git a/math/+test/floats_test.ha b/math/+test/floats_test.ha @@ -23,13 +23,6 @@ }; @test fn float_normality() void = { - assert(isnormal(0.0)); - assert(isnormal(1.0)); - assert(!isnormal(NAN)); - assert(!isnormal(INF)); - assert(!isnormal(1.0e-310)); - assert(!isnormal(1.0e-40f32)); - assert(isnormalf32(1.0)); assert(isnormalf32(0.0)); assert(!isnormalf32(NAN)); @@ -44,13 +37,6 @@ assert(!isnormalf64(-1.0e-320)); assert(isnormalf64(-1.0e-330)); - assert(issubnormal(1.0e-320)); - assert(issubnormal(1.0e-42f32)); - assert(!issubnormal(NAN)); - assert(!issubnormal(INF)); - assert(!issubnormal(1.0)); - assert(!issubnormal(0.0)); - assert(issubnormalf32(1.0e-45)); assert(issubnormalf32(-1.0e-39)); assert(!issubnormalf32(-NAN)); @@ -68,45 +54,43 @@ @test fn absf() void = { for (let idx = 0z; idx < len(TEST_INPUTS); idx += 1) { - assert(absf(TEST_INPUTS[idx]) == TEST_ABSF[idx]); + assert(absf64(TEST_INPUTS[idx]) == TEST_ABSF[idx]); }; assert(absf64(2f64) == 2f64); assert(absf32(2.0f32) == 2.0f32); - assert(absf(2f64) == 2f64); - assert(absf(2.0f32) == 2f64); - assert(absf(-2f64) == 2f64); - assert(absf(-2.0f32) == 2.0f32); - assert(absf(0f32) == 0f32); - assert(absf(0f64) == 0f64); + assert(absf64(-2f64) == 2f64); + assert(absf32(-2.0f32) == 2.0f32); + assert(absf64(0f64) == 0f64); + assert(absf32(0f32) == 0f32); }; @test fn copysign() void = { - assert(copysign(100f64, 1f64) == 100f64); - assert(copysign(100f64, -1f64) == -100f64); - assert(copysign(100.0f32, 1.0f32) == 100.0f32); - assert(copysign(100.0f32, -1.0f32) == -100.0f32); - assert(copysign(100f64, 0f64) == 100f64); - assert(copysign(100f64, -0f64) == -100f64); - assert(copysign(0f64, 100f64) == 0f64); - assert(signf(copysign(0f64, 100f64)) > 0); - assert(copysign(0f64, -100f64) == 0f64); - assert(signf(copysign(0f64, -100f64)) < 0); + assert(copysignf64(100f64, 1f64) == 100f64); + assert(copysignf64(100f64, -1f64) == -100f64); + assert(copysignf32(100.0f32, 1.0f32) == 100.0f32); + assert(copysignf32(100.0f32, -1.0f32) == -100.0f32); + assert(copysignf64(100f64, 0f64) == 100f64); + assert(copysignf64(100f64, -0f64) == -100f64); + assert(copysignf64(0f64, 100f64) == 0f64); + assert(signf64(copysignf64(0f64, 100f64)) > 0); + assert(copysignf64(0f64, -100f64) == 0f64); + assert(signf64(copysignf64(0f64, -100f64)) < 0); }; @test fn signf() void = { for (let idx = 0z; idx < len(TEST_INPUTS); idx += 1) { - assert(signf(TEST_INPUTS[idx]) == TEST_SIGNF[idx]); + assert(signf64(TEST_INPUTS[idx]) == TEST_SIGNF[idx]); }; - assert(signf(0f64) == 1i64); - assert(signf(-0f64) == -1i64); - assert(signf(0.0f32) == 1i64); - assert(signf(-0.0f32) == -1i64); - assert(signf(1.5f64) == 1i64); - assert(signf(-1.5f64) == -1i64); - assert(ispositive(1f64)); - assert(!ispositive(-1f64)); - assert(isnegative(-1f64)); - assert(!isnegative(1f64)); + assert(signf64(0f64) > 0); + assert(signf64(-0f64) < 0); + assert(signf32(0f32) > 0); + assert(signf32(-0f32) < 0); + assert(signf64(1.5f64) > 0); + assert(signf64(-1.5f64) < 0); + assert(ispositivef64(1f64)); + assert(!ispositivef64(-1f64)); + assert(isnegativef64(-1f64)); + assert(!isnegativef64(1f64)); }; @test fn normalize() void = { @@ -120,20 +104,20 @@ @test fn frexp() void = { for (let idx = 0z; idx < len(TEST_INPUTS); idx += 1) { - let res = frexp(TEST_INPUTS[idx]); + let res = frexpf64(TEST_INPUTS[idx]); let expected = TEST_FREXP[idx]; assert(res.0 == expected.0); assert(res.1 == expected.1); }; - let res = frexp(3f64); + let res = frexpf64(3f64); assert(res.0 == 0.75f64); assert(res.1 == 2i64); - res = frexp(2.42f64); + res = frexpf64(2.42f64); assert(res.0 == 0.605f64); assert(res.1 == 2i64); - res = frexp(NAN); + let res = frexpf32(NAN); assert(res.1 == 0); - res = frexp(INF); + res = frexpf32(INF); assert(res.1 == 0); }; @@ -149,7 +133,7 @@ }; assert(ldexpf64(1f64, -1076i64) == 0f64); assert(ldexpf64(-1f64, -1076i64) == -0f64); - assert(signf(ldexpf64(-1f64, -1076i64)) < 0); + assert(signf64(ldexpf64(-1f64, -1076i64)) < 0); assert(ldexpf64(2f64, 1024i64) == INF); assert(ldexpf64(-2f64, 1024i64) == -INF); @@ -164,7 +148,7 @@ }; assert(ldexpf32(1.0f32, -1076i32) == 0.0f32); assert(ldexpf32(-1.0f32, -1076i32) == -0.0f32); - assert(signf(ldexpf32(-1.0f32, -1076i32)) < 0); + assert(signf64(ldexpf32(-1.0f32, -1076i32)) < 0); assert(ldexpf32(2.0f32, 1024i32) == INF); assert(ldexpf32(-2.0f32, 1024i32) == -INF); }; @@ -188,11 +172,11 @@ res = modfracf64(0f64); assert(res.0 == 0f64); assert(res.1 == 0f64); - assert(signf(res.1) > 0); + assert(signf64(res.1) > 0); res = modfracf64(-0f64); assert(res.0 == -0f64); assert(res.1 == -0f64); - assert(signf(res.1) < 0); + assert(signf64(res.1) < 0); res = modfracf64(23.50f64); assert(res.0 == 23f64); assert(res.1 == 0.50f64); @@ -213,11 +197,11 @@ res = modfracf32(0.0f32); assert(res.0 == 0f32); assert(res.1 == 0.0f32); - assert(signf(res.1) > 0); + assert(signf64(res.1) > 0); res = modfracf32(-0.0f32); assert(res.0 == -0f32); assert(res.1 == -0.0f32); - assert(signf(res.1) < 0); + assert(signf64(res.1) < 0); res = modfracf32(23.50f32); assert(res.0 == 23f32); assert(res.1 == 0.50f32); diff --git a/math/+test/math_test.ha b/math/+test/math_test.ha @@ -2,9 +2,9 @@ // (c) Hare authors <https://harelang.org> @test fn isclose() void = { - assert(isclose(1f64, 2f64, 2f64)); - assert(isclose(1.0f32, 2.0f32, 2.0f32)); - assert(!isclose(1.0005f32, 1.0004f32, 0.00001f32)); + assert(isclosef64(1f64, 2f64, 2f64)); + assert(isclosef32(1.0f32, 2.0f32, 2.0f32)); + assert(!isclosef32(1.0005f32, 1.0004f32, 0.00001f32)); assert(isclosef64(1f64, 1.0000000000000000000000000001f64)); assert(isclosef32(1.0f32, 1.0000000000000000000000000001f32)); assert(!isclosef32(1.0005f32, 1.0004f32)); @@ -167,7 +167,7 @@ assert(powf64(0f64, 123f64) == 0f64); const neg_zero = powf64(-0f64, 123f64); assert(neg_zero == 0f64); - assert(isnegative(neg_zero)); + assert(isnegativef64(neg_zero)); // pow(±0, y) = +0 for finite y > 0 and not an odd integer assert(powf64(0f64, 8f64) == 0f64); // pow(-1, ±Inf) = 1 diff --git a/math/complex/complex.ha b/math/complex/complex.ha @@ -365,7 +365,7 @@ export fn asinhc128(z: c128) c128 = { }; } else if (math::isinf(z.1)) { return (math::copysignf64(z.1, z.0), - math::copysign(math::PI / 2f64, z.1)); + math::copysignf64(math::PI / 2f64, z.1)); }; const zz = mulc128(z, z); const z1 = (1f64 + zz.0, zz.1); // 1 + z * z @@ -586,9 +586,9 @@ fn tanSeries(z: c128) f64 = { export fn tanc128(x: c128) c128 = { if (math::isinf(x.1)) { if (math::isinf(x.0) || math::isnan(x.0)) { - return (math::copysign(0f64, x.0), math::copysign(1f64, x.1)); + return (math::copysignf64(0f64, x.0), math::copysignf64(1f64, x.1)); }; - return (math::copysign(0f64, math::sinf64(2f64*x.0)), math::copysign(1f64, x.1)); + return (math::copysignf64(0f64, math::sinf64(2f64*x.0)), math::copysignf64(1f64, x.1)); }; if (x.0 == 0f64 && math::isnan(x.1)) { return x; @@ -607,9 +607,9 @@ export fn tanc128(x: c128) c128 = { export fn tanhc128(x: c128) c128 = { if (math::isinf(x.0)) { if (math::isinf(x.1) || math::isnan(x.1)) { - return (math::copysign(1f64, x.0), math::copysign(0f64, x.1)); + return (math::copysignf64(1f64, x.0), math::copysignf64(0f64, x.1)); }; - return (math::copysign(1f64, x.0), math::copysign(0f64, math::sinf64(2f64*x.1))); + return (math::copysignf64(1f64, x.0), math::copysignf64(0f64, math::sinf64(2f64*x.1))); }; if (x.1 == 0f64 && math::isnan(x.0)) { return x; @@ -629,9 +629,9 @@ export fn atanc128(x: c128) c128 = { return (x.0, math::atanhf64(x.1)); } else if (math::isinf(x.1) || math::isinf(x.0)) { if (math::isnan(x.0)) { - return (math::NAN, math::copysign(0f64, x.1)); + return (math::NAN, math::copysignf64(0f64, x.1)); }; - return (math::copysign(math::PI/2f64, x.0), math::copysign(0f64, x.1)); + return (math::copysignf64(math::PI/2f64, x.0), math::copysignf64(0f64, x.1)); } else if (math::isnan(x.0) || math::isnan(x.1)) { return (math::NAN, math::NAN); }; diff --git a/math/floats.ha b/math/floats.ha @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 // (c) Hare authors <https://harelang.org> -use types; - // Returns the binary representation of the given f64. export fn f64bits(n: f64) u64 = *(&n: *u64); @@ -161,16 +159,6 @@ export fn isinf(n: f64) bool = { assert(!isinf(-1.23f32)); }; -// Returns true if the given floating-point number is normal. -export fn isnormal(n: types::floating) bool = { - match (n) { - case let n: f32 => - return isnormalf32(n); - case let n: f64 => - return isnormalf64(n); - }; -}; - // Returns true if the given f64 is normal. export fn isnormalf64(n: f64) bool = { const bits = f64bits(n); @@ -187,16 +175,6 @@ export fn isnormalf32(n: f32) bool = { return exp != F32_EXPONENT_MASK && (exp > 0 || mant == 0); }; -// Returns true if the given floating-point number is subnormal. -export fn issubnormal(n: types::floating) bool = { - match (n) { - case let n: f32 => - return issubnormalf32(n); - case let n: f64 => - return issubnormalf64(n); - }; -}; - // Returns true if the given f64 is subnormal. export fn issubnormalf64(n: f64) bool = { const bits = f64bits(n); @@ -229,16 +207,6 @@ export fn absf32(n: f32) f32 = { return f32frombits(f32bits(n) & ~F32_SIGN_MASK); }; -// Returns the absolute value of floating-point number n. -export fn absf(n: types::floating) f64 = { - match (n) { - case let n: f64 => - return absf64(n); - case let n: f32 => - return (absf32(n): f64); - }; -}; - // Returns 1 if x is positive and -1 if x is negative. Note that zero is also // signed. export fn signf64(x: f64) i64 = { @@ -259,49 +227,18 @@ export fn signf32(x: f32) i64 = { }; }; -// Returns 1 if x is positive and -1 if x is negative. Note that zero is also -// signed. -export fn signf(x: types::floating) i64 = { - match (x) { - case let n: f64 => - return signf64(n); - case let n: f32 => - return signf32(n); - }; -}; - // Returns whether or not x is positive. export fn ispositivef64(x: f64) bool = signf64(x) == 1i64; // Returns whether or not x is positive. export fn ispositivef32(x: f32) bool = signf32(x) == 1i32; -// Returns whether or not x is positive. -export fn ispositive(x: types::floating) bool = { - match (x) { - case let n: f64 => - return ispositivef64(n); - case let n: f32 => - return ispositivef32(n); - }; -}; - // Returns whether or not x is negative. export fn isnegativef64(x: f64) bool = signf64(x) == -1i64; // Returns whether or not x is negative. export fn isnegativef32(x: f32) bool = signf32(x) == -1i32; -// Returns whether or not x is negative. -export fn isnegative(x: types::floating) bool = { - match (x) { - case let n: f64 => - return isnegativef64(n); - case let n: f32 => - return isnegativef32(n); - }; -}; - // Returns x, but with the sign of y. export fn copysignf64(x: f64, y: f64) f64 = { return f64frombits((f64bits(x) & ~F64_SIGN_MASK) | @@ -314,16 +251,6 @@ export fn copysignf32(x: f32, y: f32) f32 = { (f32bits(y) & F32_SIGN_MASK)); }; -// Returns x, but with the sign of y. -export fn copysign(x: types::floating, y: types::floating) f64 = { - match (x) { - case let n: f64 => - return copysignf64(n, (y: f64)); - case let n: f32 => - return (copysignf32(n, (y: f32)): f64); - }; -}; - // Takes a potentially subnormal f64 n and returns a normal f64 normal_float // and an exponent exp such that n == normal_float * 2^{exp}. export fn normalizef64(n: f64) (f64, i64) = { @@ -383,18 +310,6 @@ export fn frexpf32(n: f32) (f32, i64) = { return (mantissa, exp); }; -// Breaks a float down into its mantissa and exponent. The mantissa will be -// between 0.5 and 1. -export fn frexp(n: types::floating) (f64, i64) = { - match (n) { - case let n: f64 => - return frexpf64(n); - case let n: f32 => - let (mantissa, exp) = frexpf32(n); - return (mantissa, exp); - }; -}; - // Creates an f64 from a mantissa and an exponent. export fn ldexpf64(mantissa: f64, exp: i64) f64 = { if (isnan(mantissa) || isinf(mantissa) || mantissa == 0f64) { @@ -410,7 +325,7 @@ export fn ldexpf64(mantissa: f64, exp: i64) f64 = { let res_exp = exp + normalization_exp + mantissa_exp; // Underflow if (res_exp < -(F64_EXPONENT_BIAS: i64) - (F64_MANTISSA_BITS: i64)) { - return copysign(0f64, mantissa); + return copysignf64(0f64, mantissa); }; // Overflow if (res_exp > (F64_EXPONENT_BIAS: i64)) { diff --git a/math/math.ha b/math/math.ha @@ -70,20 +70,6 @@ export fn isclosef32(x: f32, y: f32, tol: f32 = STANDARD_TOL) bool = { return absf32(x - y) < tol; }; -// Returns whether x and y are within tol of each other. -export fn isclose( - x: types::floating, - y: types::floating, - tol: types::floating, -) bool = { - match (x) { - case let n: f64 => - return isclosef64(n, y as f64, tol as f64); - case let n: f32 => - return isclosef32(n, y as f32, tol as f32); - }; -}; - // e - https://oeis.org/A001113 export def E: f64 = 2.71828182845904523536028747135266249775724709369995957496696763; // pi - https://oeis.org/A000796 @@ -181,9 +167,7 @@ export fn logf64(x: f64) f64 = { }; // Reduce - const f1_and_ki = frexp(x); - let f1 = f1_and_ki.0; - let ki = f1_and_ki.1; + const (f1, ki) = frexpf64(x); if (f1 < (SQRT_2 / 2f64)) { f1 *= 2f64; ki -= 1i64; @@ -209,9 +193,7 @@ export fn log10f64(x: f64) f64 = { // Returns the binary logarithm of x. export fn log2f64(x: f64) f64 = { - const frexp_res = frexpf64(x); - let frac = frexp_res.0; - let exp = frexp_res.1; + const (frac, exp) = frexpf64(x); // Make sure exact powers of two give an exact answer. // Don't depend on log(0.5) * (1 / LN_2) + exp being exactly exp - 1. if (frac == 0.5f64) { @@ -742,7 +724,7 @@ export fn powf64(x: f64, p: f64) f64 = { // Repeatedly square our number x, for each bit in our power p. // If the current bit is 1 in p, add the respective power of x to our // result. - let (x_mantissa, x_exp) = frexp(x); + let (x_mantissa, x_exp) = frexpf64(x); for (let i = p_int: i64; i != 0; i >>= 1) { // Check for over/underflow. if (x_exp <= -1i64 << (F64_EXPONENT_BITS: i64)) { @@ -834,18 +816,14 @@ export fn modf64(x: f64, y: f64) f64 = { y = absf64(y); - const y_frexp = frexpf64(y); - const y_frac = y_frexp.0; - const y_exp = y_frexp.1; + const (y_frac, y_exp) = frexpf64(y); let r = x; if (x < 0f64) { r = -x; }; for (r >= y) { - const r_frexp = frexpf64(r); - const r_frac = r_frexp.0; - let r_exp = r_frexp.1; + const (r_frac, r_exp) = frexpf64(r); if (r_frac < y_frac) { r_exp -= 1i64; }; diff --git a/math/trig.ha b/math/trig.ha @@ -234,7 +234,7 @@ export fn cosf64(x: f64) f64 = { // Make argument positive let is_negative = false; - x = absf(x); + x = absf64(x); let j = 0u64; let y = 0f64;