From 259112e178120c162d22a61c7bd8e40938145e29 Mon Sep 17 00:00:00 2001 From: Taylor W Date: Mon, 13 Jan 2025 06:37:49 -0600 Subject: [PATCH] math: macros to set floating-point numbers with uint (#1826) * math: Setting the bits of floating-point numbers Added macros which set all 32 bits of a float, the lower 32 bits of a double, and the upper 32 bits of a double. Some changes were made to older code to use these macros. * Replaced code with bitsetting macros in __tan.c3 and tan.c3 * math: tests for word macros and release notes Tests were written for the word macros, which include getting and setting a float with a uint and getting and setting the high or low word of a double with a uint. Release notes were updated to include the word setter macros. --- lib/std/math/math.c3 | 15 ++++++++ lib/std/math/math_nolibc/__tan.c3 | 10 +++--- lib/std/math/math_nolibc/acos.c3 | 17 +++------ lib/std/math/math_nolibc/asin.c3 | 8 ++--- lib/std/math/math_nolibc/atanh.c3 | 8 ++--- lib/std/math/math_nolibc/tan.c3 | 4 +-- releasenotes.md | 1 + test/unit/stdlib/math/math.c3 | 58 +++++++++++++++++++++++++++++++ 8 files changed, 90 insertions(+), 31 deletions(-) diff --git a/lib/std/math/math.c3 b/lib/std/math/math.c3 index c352738e5..5480a095e 100644 --- a/lib/std/math/math.c3 +++ b/lib/std/math/math.c3 @@ -1042,6 +1042,21 @@ macro uint double.high_word(double d) => (uint)(bitcast(d, ulong) >> 32); macro uint double.low_word(double d) => (uint)bitcast(d, ulong); macro uint float.word(float d) => bitcast(d, uint); +macro void double.set_high_word(double* d, uint u) +{ + ulong rep = bitcast(*d, ulong); + rep = ((ulong)u << 32) | (rep & 0xffffffff); + *d = bitcast(rep, double); +} + +macro void double.set_low_word(double* d, uint u) +{ + ulong rep = bitcast(*d, ulong); + rep = (rep & 0xffffffff00000000) | (ulong)u; + *d = bitcast(rep, double); +} + +macro void float.set_word(float* f, uint u) => *f = bitcast(u, float); macro double scalbn(double x, int n) => _scalbn(x, n); diff --git a/lib/std/math/math_nolibc/__tan.c3 b/lib/std/math/math_nolibc/__tan.c3 index 3952d2a12..83da7ed19 100644 --- a/lib/std/math/math_nolibc/__tan.c3 +++ b/lib/std/math/math_nolibc/__tan.c3 @@ -32,7 +32,7 @@ fn double __tan(double x, double y, int odd) @extern("__tan") @weak @nostrip const double PIO4 = 7.85398163397448278999e-01; /* 3FE921FB, 54442D18 */ const double PIO4LO = 3.06161699786838301793e-17; /* 3C81A626, 33145C07 */ - uint hx = (uint)(bitcast(x, ulong) >> 32); + uint hx = x.high_word(); bool big = (hx &0x7fffffff) >= 0x3FE59428; // |x| >= 0.6744 int sign @noinit; if (big) @@ -68,12 +68,12 @@ fn double __tan(double x, double y, int odd) @extern("__tan") @weak @nostrip // -1.0/(x+r) has up to 2ulp error, so compute it accurately // Clear low word - ulong d = bitcast(w, ulong) & 0xFFFF_FFFF_0000_0000; - double w0 = bitcast(d, double); + double w0 = w; + w0.set_low_word(0); v = r - (w0 - x); // w0+v = r+x double a = -1.0 / w; - d = bitcast(a, ulong) & 0xFFFF_FFFF_0000_0000; - double a0 = bitcast(d, double); + double a0 = a; + a0.set_low_word(0); return a0 + a * (1.0 + a0 * w0 + a0 * v); } diff --git a/lib/std/math/math_nolibc/acos.c3 b/lib/std/math/math_nolibc/acos.c3 index e638186a2..1245a24ff 100644 --- a/lib/std/math/math_nolibc/acos.c3 +++ b/lib/std/math/math_nolibc/acos.c3 @@ -63,12 +63,8 @@ fn double _acos(double x) @weak @extern("acos") @nostrip default: double z = (1. - x) * 0.5; double s = math::sqrt(z); - double df @noinit; - { - ulong rep = bitcast(s, ulong); - rep &= 0xffffffff00000000; - df = bitcast(rep, double); - } + double df = s; + df.set_low_word(0); double c = (z - df * df) / (s + df); double w = _r(z) * s + c; return 2. * (df + w); @@ -133,12 +129,9 @@ fn float _acosf(float x) @weak @extern("acosf") @nostrip default: float z = (1.f - x) * 0.5f; float s = math::sqrt(z); - float df @noinit; - { - uint rep = bitcast(s, uint); - rep &= 0xfffff000; - df = bitcast(rep, float); - } + float df = s; + uint idf = df.word(); + df.set_word(idf & 0xfffff000); float c = (z - df * df) / (s + df); float w = _r_f(z) * s + c; return 2.f * (df + w); diff --git a/lib/std/math/math_nolibc/asin.c3 b/lib/std/math/math_nolibc/asin.c3 index e0a9f469b..281107c82 100644 --- a/lib/std/math/math_nolibc/asin.c3 +++ b/lib/std/math/math_nolibc/asin.c3 @@ -64,12 +64,8 @@ fn double _asin(double x) @weak @extern("asin") @nostrip } else { - double f @noinit; - { - ulong rep = bitcast(s, ulong); - rep &= 0xffffffff00000000; - f = bitcast(rep, double); - } + double f = s; + f.set_low_word(0); double c = (z - f * f) / (s + f); x = 0.5 * PIO2_HI - (2. * s * r - (PIO2_LO - 2. * c) - (0.5 * PIO2_HI - 2. * f)); } diff --git a/lib/std/math/math_nolibc/atanh.c3 b/lib/std/math/math_nolibc/atanh.c3 index 152e218c0..5f7383569 100644 --- a/lib/std/math/math_nolibc/atanh.c3 +++ b/lib/std/math/math_nolibc/atanh.c3 @@ -32,11 +32,7 @@ fn double _atanh(double x) @weak @extern("atanh") @nostrip case ix < 0x3e300000 && (1e300 + x) > 0.: return x; } - { - ulong rep = bitcast(x, ulong); - rep = (rep & 0x00000000ffffffff) | (ulong)ix << 32; - x = bitcast(rep, double); - } + x.set_high_word(ix); /* |x| < 0.5 */ if (ix < 0x3fe00000) { @@ -84,7 +80,7 @@ fn float _atanhf(float x) @weak @extern("atanhf") @nostrip case ix < 0x31800000 && (1e30 + x) > 0.f: return x; } - x = bitcast(ix, float); + x.set_word(ix); /* |x| < 0.5 */ if (ix < 0x3f000000) { diff --git a/lib/std/math/math_nolibc/tan.c3 b/lib/std/math/math_nolibc/tan.c3 index 7d871d609..056329492 100644 --- a/lib/std/math/math_nolibc/tan.c3 +++ b/lib/std/math/math_nolibc/tan.c3 @@ -14,7 +14,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); fn double tan(double x) @extern("tan") @weak @nostrip { - uint ix = (uint)(bitcast(x, ulong) >> 32); + uint ix = x.high_word(); ix &= 0x7fffffff; // |x| ~< pi/4 @@ -59,7 +59,7 @@ fn double tan(double x) @extern("tan") @weak @nostrip fn float tanf(float x) @extern("tanf") @weak @nostrip { - uint ix = bitcast(x, uint); + uint ix = x.word(); uint sign = ix >> 31; ix &= 0x7fffffff; diff --git a/releasenotes.md b/releasenotes.md index c1fcf572f..8a2bd7547 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -96,6 +96,7 @@ - Deprecation of several `&` macros. - Format functions for timedates. - Add `@assert_leak()` to assert on memory leaks in the scope. +- Added `double.set_high_word()`, `double.set_low_word()`, and `float.set_word()`. ## 0.6.5 Change list diff --git a/test/unit/stdlib/math/math.c3 b/test/unit/stdlib/math/math.c3 index 98b5acbc7..edf9a4851 100644 --- a/test/unit/stdlib/math/math.c3 +++ b/test/unit/stdlib/math/math.c3 @@ -190,6 +190,64 @@ fn void test_atanh() @test } } +fn void test_floating_point_word() @test +{ + float f = 1.f; + assert(f.word() == 0x3f800000); + // xor swap + float f1 = 2.f; + float f2 = 3.f; + uint u1 = f1.word(); + uint u2 = f2.word(); + u1 ^= u2; u2 ^= u1; u1 ^= u2; + f1.set_word(u1); + f2.set_word(u2); + assert((f1 == 3.f) && (f2 == 2.f)); + // sign bit trick + f = -1.f; + assert((f.word() >> 31) == 1); + f = 1.f; + assert((f.word() >> 31) == 0); + // absolute value bit trick + float[<4>] fvals = { 1.f, -1.f, 91.5f, -91.5f }; + for (int i = 0; i < 4; i++) + { + f = fvals[i]; + f.set_word(f.word() & 0x7fffffff); + assert(f == math::abs(fvals[i])); + } + + double d = 1.; + assert((d.high_word() - 0x3ff00000 | d.low_word()) == 0); + // xor swap + double d1 = 2.0; + double d2 = 3.0; + uint u1_low = d1.low_word(); + uint u2_low = d2.low_word(); + u1_low ^= u2_low; u2_low ^= u1_low; u1_low ^= u2_low; + uint u1_high = d1.high_word(); + uint u2_high = d2.high_word(); + u1_high ^= u2_high; u2_high ^= u1_high; u1_high ^= u2_high; + d1.set_low_word(u1_low); + d1.set_high_word(u1_high); + d2.set_low_word(u2_low); + d2.set_high_word(u2_high); + assert((d1 == 3.) && (d2 == 2.)); + // sign bit trick + d = -1.; + assert((d.high_word() >> 31) == 1); + d = 1.; + assert((d.high_word() >> 31) == 0); + // absolute value bit trick + double[<4>] vals = { 1., -1., 91.5, -91.5 }; + for (int i = 0; i < 4; i++) + { + d = vals[i]; + d.set_high_word(d.high_word() & 0x7fffffff); + assert(d == math::abs(vals[i])); + } +} + fn void test_ceil() @test { double d = -123.1;