commit 30f4370c24502f80c84f681d366e25a6710a9269
parent 58498b4d24667dd39a342c2555424229feb0e636
Author: Bor Grošelj Simić <bor.groseljsimic@telemach.net>
Date: Thu, 10 Feb 2022 01:32:50 +0100
math: floating point exceptions and rounding modes
Signed-off-by: Bor Grošelj Simić <bor.groseljsimic@telemach.net>
Diffstat:
9 files changed, 637 insertions(+), 1 deletion(-)
diff --git a/math/fenv+aarch64.ha b/math/fenv+aarch64.ha
@@ -0,0 +1,191 @@
+// Defines flags characterizing types of floating point exceptions,
+// Each of the flags is only defined when the target platform supports handling
+// the corresponding exception. Flags NONE and ALL are always
+// defined and correspond to a bitwise OR of none and all defined flags
+// respectively. Platforms may define additional nonstandard flags.
+//
+// Examples:
+// math::raiseexcept(math::fexcept::UNDERFLOW); // raise UNDERFLOW
+// math::clearexcept(math::fexcept::ALL); // clear all exceptions
+//
+// // e will be math::fexcept::INVALID
+// math::clearexcept(math::fexcept::ALL);
+// let a = 0.0/0.0;
+// let e = math::testexcept(math::fexcept::INVALID | math::fexcept::INEXACT);
+export type fexcept = enum uint {
+ // No flags set
+ NONE = 0,
+ // Occurs when there is no well-defined result of an operation, such as
+ // with 0/0 or sqrt(-1)
+ INVALID = 1 << 0,
+ // Occurs when an operation on finite numbers produces infinity
+ DIVBYZERO = 1 << 1,
+ // Occurs when the result of an operation is much bigger (by
+ // absolute value) than the biggest representable finite number
+ OVERFLOW = 1 << 2,
+ // Occurs when the result of an operation is too small (by
+ // absolute value) to be stored as a normalized number
+ UNDERFLOW = 1 << 3,
+ // Occurs when the result of an operation is rounded to a
+ // value that differs from the infinite precision result.
+ INEXACT = 1 << 4,
+ // Combination of all flags
+ ALL = INVALID | DIVBYZERO | OVERFLOW | UNDERFLOW | INEXACT,
+};
+
+// Defines values characterizing different floating point rounding behaviors.
+// Each of the values is only definined when the target platform supports the
+// corresponding rounding mode.
+export type fround = enum uint {
+ // Round towards nearest integer, with ties rounding to even
+ TONEAREST = 0,
+ // Round towards negative infinity
+ DOWNWARD = 0x800000,
+ // Round towards positive infinity
+ UPWARD = 0x400000,
+ // Round towards zero
+ TOWARDZERO = 0xC00000,
+};
+
+@test fn fexcept() void = {
+ assert(testexcept(fexcept::ALL) == fexcept::NONE);
+ assert(testexcept(fexcept::NONE) == fexcept::NONE);
+
+ raiseexcept(fexcept::INEXACT | fexcept::DIVBYZERO);
+
+ assert(testexcept(fexcept::INEXACT) == fexcept::INEXACT);
+ assert(testexcept(fexcept::DIVBYZERO) == fexcept::DIVBYZERO);
+ assert(testexcept(fexcept::UNDERFLOW) == fexcept::NONE);
+ assert(testexcept(fexcept::DIVBYZERO | fexcept::INEXACT)
+ == fexcept::DIVBYZERO | fexcept::INEXACT);
+ assert(testexcept(fexcept::DIVBYZERO | fexcept::INEXACT | fexcept::INVALID)
+ == fexcept::DIVBYZERO | fexcept::INEXACT);
+
+ clearexcept(fexcept::INEXACT);
+
+ assert(testexcept(fexcept::DIVBYZERO | fexcept::INEXACT) == fexcept::DIVBYZERO);
+
+ raiseexcept(fexcept::ALL);
+
+ assert(testexcept(fexcept::ALL) == fexcept::ALL);
+ assert(testexcept(fexcept::NONE) == fexcept::NONE);
+
+ clearexcept(fexcept::ALL);
+
+ assert(testexcept(fexcept::ALL) == fexcept::NONE);
+ assert(testexcept(fexcept::NONE) == fexcept::NONE);
+};
+
+@test fn fround() void = {
+ // from musl's testsuite
+ let f = &f64frombits;
+
+ assert(getround() == fround::TONEAREST);
+ assert(isnan(nearbyintf64(f(0x7ff8000000000000))));
+ assert(isposinf(nearbyintf64(f(0x7ff0000000000000))));
+ assert(isneginf(nearbyintf64(f(0xfff0000000000000))));
+ assert(nearbyintf64(f(0x0)) == f(0x0));
+ assert(nearbyintf64(f(0x8000000000000000)) == f(0x8000000000000000));
+ assert(nearbyintf64(f(0x3ff0000000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbff0000000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x3fe0000000000000)) == f(0x0));
+ assert(nearbyintf64(f(0xbfe0000000000000)) == f(0x8000000000000000));
+ assert(nearbyintf64(f(0x3ff0001000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbff0001000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x3feffff000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbfeffff000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x39b0000000000000)) == f(0x0));
+ assert(nearbyintf64(f(0xb9b0000000000000)) == f(0x8000000000000000));
+
+ setround(fround::DOWNWARD);
+ assert(getround() == fround::DOWNWARD);
+ assert(isnan(nearbyintf64(f(0x7ff8000000000000))));
+ assert(isposinf(nearbyintf64(f(0x7ff0000000000000))));
+ assert(isneginf(nearbyintf64(f(0xfff0000000000000))));
+ assert(nearbyintf64(f(0x0)) == f(0x0));
+ assert(nearbyintf64(f(0x8000000000000000)) == f(0x8000000000000000));
+ assert(nearbyintf64(f(0x3ff0000000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbff0000000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x3fe0000000000000)) == f(0x0));
+ assert(nearbyintf64(f(0xbfe0000000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x3ff0001000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbff0001000000000)) == f(0xc000000000000000));
+ assert(nearbyintf64(f(0x3feffff000000000)) == f(0x0));
+ assert(nearbyintf64(f(0xbfeffff000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x39b0000000000000)) == f(0x0));
+ assert(nearbyintf64(f(0xb9b0000000000000)) == f(0xbff0000000000000));
+
+ setround(fround::UPWARD);
+ assert(getround() == fround::UPWARD);
+ assert(isnan(nearbyintf64(f(0x7ff8000000000000))));
+ assert(isposinf(nearbyintf64(f(0x7ff0000000000000))));
+ assert(isneginf(nearbyintf64(f(0xfff0000000000000))));
+ assert(nearbyintf64(f(0x0)) == f(0x0));
+ assert(nearbyintf64(f(0x8000000000000000)) == f(0x8000000000000000));
+ assert(nearbyintf64(f(0x3ff0000000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbff0000000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x3fe0000000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbfe0000000000000)) == f(0x8000000000000000));
+ assert(nearbyintf64(f(0x3ff0001000000000)) == f(0x4000000000000000));
+ assert(nearbyintf64(f(0xbff0001000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x3feffff000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbfeffff000000000)) == f(0x8000000000000000));
+ assert(nearbyintf64(f(0x39b0000000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xb9b0000000000000)) == f(0x8000000000000000));
+
+ let f = &f32frombits;
+
+ setround(fround::TONEAREST);
+ assert(getround() == fround::TONEAREST);
+ assert(isnan(nearbyintf32(f(0x7fc00000))));
+ assert(isposinf(nearbyintf32(f(0x7f800000))));
+ assert(isneginf(nearbyintf32(f(0xff800000))));
+ assert(nearbyintf32(f(0x0)) == f(0x0));
+ assert(nearbyintf32(f(0x80000000)) == f(0x80000000));
+ assert(nearbyintf32(f(0x3f800000)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf800000)) == f(0xbf800000));
+ assert(nearbyintf32(f(0x3f000000)) == f(0x0));
+ assert(nearbyintf32(f(0xbf000000)) == f(0x80000000));
+ assert(nearbyintf32(f(0x3f800080)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf800080)) == f(0xbf800000));
+ assert(nearbyintf32(f(0x3f7fff80)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf7fff80)) == f(0xbf800000));
+ assert(nearbyintf32(f(0xd800000)) == f(0x0));
+ assert(nearbyintf32(f(0x8d800000)) == f(0x80000000));
+
+ setround(fround::DOWNWARD);
+ assert(getround() == fround::DOWNWARD);
+ assert(isnan(nearbyintf32(f(0x7fc00000))));
+ assert(isposinf(nearbyintf32(f(0x7f800000))));
+ assert(isneginf(nearbyintf32(f(0xff800000))));
+ assert(nearbyintf32(f(0x0)) == f(0x0));
+ assert(nearbyintf32(f(0x80000000)) == f(0x80000000));
+ assert(nearbyintf32(f(0x3f800000)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf800000)) == f(0xbf800000));
+ assert(nearbyintf32(f(0x3f000000)) == f(0x0));
+ assert(nearbyintf32(f(0xbf000000)) == f(0xbf800000));
+ assert(nearbyintf32(f(0x3f800080)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf800080)) == f(0xc0000000));
+ assert(nearbyintf32(f(0x3f7fff80)) == f(0x0));
+ assert(nearbyintf32(f(0xbf7fff80)) == f(0xbf800000));
+ assert(nearbyintf32(f(0xd800000)) == f(0x0));
+ assert(nearbyintf32(f(0x8d800000)) == f(0xbf800000));
+
+ setround(fround::UPWARD);
+ assert(getround() == fround::UPWARD);
+ assert(isnan(nearbyintf32(f(0x7fc00000))));
+ assert(isposinf(nearbyintf32(f(0x7f800000))));
+ assert(isneginf(nearbyintf32(f(0xff800000))));
+ assert(nearbyintf32(f(0x0)) == f(0x0));
+ assert(nearbyintf32(f(0x80000000)) == f(0x80000000));
+ assert(nearbyintf32(f(0x3f800000)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf800000)) == f(0xbf800000));
+ assert(nearbyintf32(f(0x3f000000)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf000000)) == f(0x80000000));
+ assert(nearbyintf32(f(0x3f800080)) == f(0x40000000));
+ assert(nearbyintf32(f(0xbf800080)) == f(0xbf800000));
+ assert(nearbyintf32(f(0x3f7fff80)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf7fff80)) == f(0x80000000));
+ assert(nearbyintf32(f(0xd800000)) == f(0x3f800000));
+ assert(nearbyintf32(f(0x8d800000)) == f(0x80000000));
+};
diff --git a/math/fenv+x86_64.ha b/math/fenv+x86_64.ha
@@ -0,0 +1,192 @@
+// Defines flags characterizing types of floating point exceptions,
+// Each of the flags is only defined when the target platform supports handling
+// the corresponding exception. Flags NONE and ALL are always
+// defined and correspond to a bitwise OR of none and all defined flags
+// respectively. Platforms may define additional nonstandard flags.
+//
+// Examples:
+// math::raiseexcept(math::fexcept::UNDERFLOW); // raise UNDERFLOW
+// math::clearexcept(math::fexcept::ALL); // clear all exceptions
+//
+// // e will be math::fexcept::INVALID
+// math::clearexcept(math::fexcept::ALL);
+// let a = 0.0/0.0;
+// let e = math::testexcept(math::fexcept::INVALID | math::fexcept::INEXACT);
+export type fexcept = enum uint {
+ // No flags set
+ NONE = 0,
+ // Occurs when there is no well-defined result of an operation, such as
+ // with 0/0 or sqrt(-1)
+ INVALID = 1 << 0,
+ __DENORM = 1 << 1, // arch-specific
+ // Occurs when an operation on finite numbers produces infinity
+ DIVBYZERO = 1 << 2,
+ // Occurs when the result of an operation is much bigger (by
+ // absolute value) than the biggest representable finite number
+ OVERFLOW = 1 << 3,
+ // Occurs when the result of an operation is too small (by
+ // absolute value) to be stored as a normalized number
+ UNDERFLOW = 1 << 4,
+ // Occurs when the result of an operation is rounded to a
+ // value that differs from the infinite precision result.
+ INEXACT = 1 << 5,
+ // Combination of all flags
+ ALL = INVALID | __DENORM | DIVBYZERO | OVERFLOW | UNDERFLOW | INEXACT,
+};
+
+// Defines values characterizing different floating point rounding behaviors.
+// Each of the values is only definined when the target platform supports the
+// corresponding rounding mode.
+export type fround = enum uint {
+ // Round towards nearest integer, with ties rounding to even
+ TONEAREST = 0,
+ // Round towards negative infinity
+ DOWNWARD = 0x400,
+ // Round towards positive infinity
+ UPWARD = 0x800,
+ // Round towards zero
+ TOWARDZERO = 0xC00,
+};
+
+@test fn fexcept() void = {
+ assert(testexcept(fexcept::ALL) == fexcept::NONE);
+ assert(testexcept(fexcept::NONE) == fexcept::NONE);
+
+ raiseexcept(fexcept::INEXACT | fexcept::DIVBYZERO);
+
+ assert(testexcept(fexcept::INEXACT) == fexcept::INEXACT);
+ assert(testexcept(fexcept::DIVBYZERO) == fexcept::DIVBYZERO);
+ assert(testexcept(fexcept::UNDERFLOW) == fexcept::NONE);
+ assert(testexcept(fexcept::DIVBYZERO | fexcept::INEXACT)
+ == fexcept::DIVBYZERO | fexcept::INEXACT);
+ assert(testexcept(fexcept::DIVBYZERO | fexcept::INEXACT | fexcept::INVALID)
+ == fexcept::DIVBYZERO | fexcept::INEXACT);
+
+ clearexcept(fexcept::INEXACT);
+
+ assert(testexcept(fexcept::DIVBYZERO | fexcept::INEXACT) == fexcept::DIVBYZERO);
+
+ raiseexcept(fexcept::ALL);
+
+ assert(testexcept(fexcept::ALL) == fexcept::ALL);
+ assert(testexcept(fexcept::NONE) == fexcept::NONE);
+
+ clearexcept(fexcept::ALL);
+
+ assert(testexcept(fexcept::ALL) == fexcept::NONE);
+ assert(testexcept(fexcept::NONE) == fexcept::NONE);
+};
+
+@test fn fround() void = {
+ // from musl's testsuite
+ let f = &f64frombits;
+
+ assert(getround() == fround::TONEAREST);
+ assert(isnan(nearbyintf64(f(0x7ff8000000000000))));
+ assert(isposinf(nearbyintf64(f(0x7ff0000000000000))));
+ assert(isneginf(nearbyintf64(f(0xfff0000000000000))));
+ assert(nearbyintf64(f(0x0)) == f(0x0));
+ assert(nearbyintf64(f(0x8000000000000000)) == f(0x8000000000000000));
+ assert(nearbyintf64(f(0x3ff0000000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbff0000000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x3fe0000000000000)) == f(0x0));
+ assert(nearbyintf64(f(0xbfe0000000000000)) == f(0x8000000000000000));
+ assert(nearbyintf64(f(0x3ff0001000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbff0001000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x3feffff000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbfeffff000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x39b0000000000000)) == f(0x0));
+ assert(nearbyintf64(f(0xb9b0000000000000)) == f(0x8000000000000000));
+
+ setround(fround::DOWNWARD);
+ assert(getround() == fround::DOWNWARD);
+ assert(isnan(nearbyintf64(f(0x7ff8000000000000))));
+ assert(isposinf(nearbyintf64(f(0x7ff0000000000000))));
+ assert(isneginf(nearbyintf64(f(0xfff0000000000000))));
+ assert(nearbyintf64(f(0x0)) == f(0x0));
+ assert(nearbyintf64(f(0x8000000000000000)) == f(0x8000000000000000));
+ assert(nearbyintf64(f(0x3ff0000000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbff0000000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x3fe0000000000000)) == f(0x0));
+ assert(nearbyintf64(f(0xbfe0000000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x3ff0001000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbff0001000000000)) == f(0xc000000000000000));
+ assert(nearbyintf64(f(0x3feffff000000000)) == f(0x0));
+ assert(nearbyintf64(f(0xbfeffff000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x39b0000000000000)) == f(0x0));
+ assert(nearbyintf64(f(0xb9b0000000000000)) == f(0xbff0000000000000));
+
+ setround(fround::UPWARD);
+ assert(getround() == fround::UPWARD);
+ assert(isnan(nearbyintf64(f(0x7ff8000000000000))));
+ assert(isposinf(nearbyintf64(f(0x7ff0000000000000))));
+ assert(isneginf(nearbyintf64(f(0xfff0000000000000))));
+ assert(nearbyintf64(f(0x0)) == f(0x0));
+ assert(nearbyintf64(f(0x8000000000000000)) == f(0x8000000000000000));
+ assert(nearbyintf64(f(0x3ff0000000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbff0000000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x3fe0000000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbfe0000000000000)) == f(0x8000000000000000));
+ assert(nearbyintf64(f(0x3ff0001000000000)) == f(0x4000000000000000));
+ assert(nearbyintf64(f(0xbff0001000000000)) == f(0xbff0000000000000));
+ assert(nearbyintf64(f(0x3feffff000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xbfeffff000000000)) == f(0x8000000000000000));
+ assert(nearbyintf64(f(0x39b0000000000000)) == f(0x3ff0000000000000));
+ assert(nearbyintf64(f(0xb9b0000000000000)) == f(0x8000000000000000));
+
+ let f = &f32frombits;
+
+ setround(fround::TONEAREST);
+ assert(getround() == fround::TONEAREST);
+ assert(isnan(nearbyintf32(f(0x7fc00000))));
+ assert(isposinf(nearbyintf32(f(0x7f800000))));
+ assert(isneginf(nearbyintf32(f(0xff800000))));
+ assert(nearbyintf32(f(0x0)) == f(0x0));
+ assert(nearbyintf32(f(0x80000000)) == f(0x80000000));
+ assert(nearbyintf32(f(0x3f800000)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf800000)) == f(0xbf800000));
+ assert(nearbyintf32(f(0x3f000000)) == f(0x0));
+ assert(nearbyintf32(f(0xbf000000)) == f(0x80000000));
+ assert(nearbyintf32(f(0x3f800080)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf800080)) == f(0xbf800000));
+ assert(nearbyintf32(f(0x3f7fff80)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf7fff80)) == f(0xbf800000));
+ assert(nearbyintf32(f(0xd800000)) == f(0x0));
+ assert(nearbyintf32(f(0x8d800000)) == f(0x80000000));
+
+ setround(fround::DOWNWARD);
+ assert(getround() == fround::DOWNWARD);
+ assert(isnan(nearbyintf32(f(0x7fc00000))));
+ assert(isposinf(nearbyintf32(f(0x7f800000))));
+ assert(isneginf(nearbyintf32(f(0xff800000))));
+ assert(nearbyintf32(f(0x0)) == f(0x0));
+ assert(nearbyintf32(f(0x80000000)) == f(0x80000000));
+ assert(nearbyintf32(f(0x3f800000)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf800000)) == f(0xbf800000));
+ assert(nearbyintf32(f(0x3f000000)) == f(0x0));
+ assert(nearbyintf32(f(0xbf000000)) == f(0xbf800000));
+ assert(nearbyintf32(f(0x3f800080)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf800080)) == f(0xc0000000));
+ assert(nearbyintf32(f(0x3f7fff80)) == f(0x0));
+ assert(nearbyintf32(f(0xbf7fff80)) == f(0xbf800000));
+ assert(nearbyintf32(f(0xd800000)) == f(0x0));
+ assert(nearbyintf32(f(0x8d800000)) == f(0xbf800000));
+
+ setround(fround::UPWARD);
+ assert(getround() == fround::UPWARD);
+ assert(isnan(nearbyintf32(f(0x7fc00000))));
+ assert(isposinf(nearbyintf32(f(0x7f800000))));
+ assert(isneginf(nearbyintf32(f(0xff800000))));
+ assert(nearbyintf32(f(0x0)) == f(0x0));
+ assert(nearbyintf32(f(0x80000000)) == f(0x80000000));
+ assert(nearbyintf32(f(0x3f800000)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf800000)) == f(0xbf800000));
+ assert(nearbyintf32(f(0x3f000000)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf000000)) == f(0x80000000));
+ assert(nearbyintf32(f(0x3f800080)) == f(0x40000000));
+ assert(nearbyintf32(f(0xbf800080)) == f(0xbf800000));
+ assert(nearbyintf32(f(0x3f7fff80)) == f(0x3f800000));
+ assert(nearbyintf32(f(0xbf7fff80)) == f(0x80000000));
+ assert(nearbyintf32(f(0xd800000)) == f(0x3f800000));
+ assert(nearbyintf32(f(0x8d800000)) == f(0x80000000));
+};
diff --git a/math/fenv_func.ha b/math/fenv_func.ha
@@ -0,0 +1,19 @@
+use rt;
+
+// Accepts a set of flags from [[fexcept]] ORed together and clears
+// exceptions corresponding to the given flags.
+export fn clearexcept(ex: fexcept) void = rt::feclearexcept(ex: uint);
+
+// Accepts a set of flags from [[fexcept]] ORed together and raises
+// exceptions corresponding to the given flags.
+export fn raiseexcept(ex: fexcept) void = rt::feraiseexcept(ex: uint);
+
+// Accepts a set of flags from [[except]] ORed together and returns
+// the subset that is currently raised
+export fn testexcept(ex: fexcept) fexcept = rt::fetestexcept(ex: uint): fexcept;
+
+// Returns the value corresponding to the current rounding mode
+export fn getround() fround = rt::fegetround(): fround;
+
+// Sets the rounding mode to the specified value
+export fn setround(mode: fround) void = rt::fesetround(mode: uint);
diff --git a/rt/+aarch64/fenv.s b/rt/+aarch64/fenv.s
@@ -0,0 +1,92 @@
+# This file is vendored from musl and is licensed under MIT license:
+#
+# ----------------------------------------------------------------------
+# Copyright © 2005-2020 Rich Felker, et al.
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+# ----------------------------------------------------------------------
+
+.global rt.fegetround
+.type rt.fegetround,%function
+rt.fegetround:
+ mrs x0, fpcr
+ and w0, w0, #0xc00000
+ ret
+
+.global rt.fesetround
+.type rt.fesetround,%function
+rt.fesetround:
+ mrs x1, fpcr
+ bic w1, w1, #0xc00000
+ orr w1, w1, w0
+ msr fpcr, x1
+ mov w0, #0
+ ret
+
+.global rt.fetestexcept
+.type rt.fetestexcept,%function
+rt.fetestexcept:
+ and w0, w0, #0x1f
+ mrs x1, fpsr
+ and w0, w0, w1
+ ret
+
+.global rt.feclearexcept
+.type rt.feclearexcept,%function
+rt.feclearexcept:
+ and w0, w0, #0x1f
+ mrs x1, fpsr
+ bic w1, w1, w0
+ msr fpsr, x1
+ mov w0, #0
+ ret
+
+.global rt.feraiseexcept
+.type rt.feraiseexcept,%function
+rt.feraiseexcept:
+ and w0, w0, #0x1f
+ mrs x1, fpsr
+ orr w1, w1, w0
+ msr fpsr, x1
+ mov w0, #0
+ ret
+
+.global rt.fegetenv
+.type rt.fegetenv,%function
+rt.fegetenv:
+ mrs x1, fpcr
+ mrs x2, fpsr
+ stp w1, w2, [x0]
+ mov w0, #0
+ ret
+
+// TODO preserve some bits
+.global rt.fesetenv
+.type rt.fesetenv,%function
+rt.fesetenv:
+ mov x1, #0
+ mov x2, #0
+ cmn x0, #1
+ b.eq 1f
+ ldp w1, w2, [x0]
+1: msr fpcr, x1
+ msr fpsr, x2
+ mov w0, #0
+ ret
diff --git a/rt/+test/run.ha b/rt/+test/run.ha
@@ -25,6 +25,7 @@ export fn tests_main() size = {
let failures: [](str, abort_reason) = [];
let npass = 0z, nfail = 0z;
+ let default_round = fegetround();
print("Running ");
print(ztos(ntest));
print(" tests:\n\n");
@@ -43,6 +44,10 @@ export fn tests_main() size = {
print("FAIL\n");
continue;
};
+
+ fesetround(default_round);
+ feclearexcept(~0u);
+
test_start[i].func();
npass += 1;
diff --git a/rt/+x86_64/fenv.s b/rt/+x86_64/fenv.s
@@ -0,0 +1,95 @@
+# This file is vendored from musl and is licensed under MIT license:
+#
+# ----------------------------------------------------------------------
+# Copyright © 2005-2020 Rich Felker, et al.
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+# ----------------------------------------------------------------------
+
+.global rt.feclearexcept
+.type rt.feclearexcept,@function
+rt.feclearexcept:
+ # maintain exceptions in the sse mxcsr, clear x87 exceptions
+ mov %edi,%ecx
+ and $0x3f,%ecx
+ fnstsw %ax
+ test %eax,%ecx
+ jz 1f
+ fnclex
+1: stmxcsr -8(%rsp)
+ and $0x3f,%eax
+ or %eax,-8(%rsp)
+ test %ecx,-8(%rsp)
+ jz 1f
+ not %ecx
+ and %ecx,-8(%rsp)
+ ldmxcsr -8(%rsp)
+1: xor %eax,%eax
+ ret
+
+.global rt.feraiseexcept
+.type rt.feraiseexcept,@function
+rt.feraiseexcept:
+ and $0x3f,%edi
+ stmxcsr -8(%rsp)
+ or %edi,-8(%rsp)
+ ldmxcsr -8(%rsp)
+ xor %eax,%eax
+ ret
+
+.global rt.fesetround
+.type rt.fesetround,@function
+rt.fesetround:
+ push %rax
+ xor %eax,%eax
+ mov %edi,%ecx
+ fnstcw (%rsp)
+ andb $0xf3,1(%rsp)
+ or %ch,1(%rsp)
+ fldcw (%rsp)
+ stmxcsr (%rsp)
+ shl $3,%ch
+ andb $0x9f,1(%rsp)
+ or %ch,1(%rsp)
+ ldmxcsr (%rsp)
+ pop %rcx
+ ret
+
+.global rt.fegetround
+.type rt.fegetround,@function
+rt.fegetround:
+ push %rax
+ stmxcsr (%rsp)
+ pop %rax
+ shr $3,%eax
+ and $0xc00,%eax
+ ret
+
+.global rt.fetestexcept
+.type rt.fetestexcept,@function
+rt.fetestexcept:
+ and $0x3f,%edi
+ push %rax
+ stmxcsr (%rsp)
+ pop %rsi
+ fnstsw %ax
+ or %esi,%eax
+ and %edi,%eax
+ ret
diff --git a/rt/fenv_defs.ha b/rt/fenv_defs.ha
@@ -0,0 +1,8 @@
+export fn feclearexcept(ex: uint) void;
+export fn feraiseexcept(ex: uint) void;
+export fn fetestexcept(ex: uint) uint;
+
+export fn fegetround() uint;
+export fn fesetround(mode: uint) void;
+
+// TODO fesetenv/fegetenv?
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -34,6 +34,7 @@ gensrcs_rt() {
+linux/socket.ha \
'+$(ARCH)'/jmp.ha \
'+$(ARCH)'/backtrace.ha \
+ fenv_defs.ha \
ensure.ha \
jmp.ha \
malloc.ha \
@@ -55,6 +56,7 @@ gensrcs_rt() {
+freebsd/types.ha \
+'$(ARCH)'/jmp.ha \
+'$(ARCH)'/backtrace.ha \
+ fenv_defs.ha \
ensure.ha \
jmp.ha \
malloc.ha \
@@ -95,6 +97,7 @@ ${stdlib}_asm=\$($cache)/rt/syscall.o \\
\$($cache)/rt/longjmp.o \\
\$($cache)/rt/restore.o \\
\$($cache)/rt/getfp.o \\
+ \$($cache)/rt/fenv.o \\
\$($cache)/rt/start.o
\$($cache)/rt/syscall.o: \$(STDLIB)/rt/+\$(PLATFORM)/syscall+\$(ARCH).s
@@ -117,6 +120,11 @@ ${stdlib}_asm=\$($cache)/rt/syscall.o \\
@mkdir -p \$($cache)/rt
@\$(AS) -o \$@ \$<
+\$($cache)/rt/fenv.o: \$(STDLIB)/rt/+\$(ARCH)/fenv.s
+ @printf 'AS \t\$@\n'
+ @mkdir -p \$($cache)/rt
+ @\$(AS) -o \$@ \$<
+
\$($cache)/rt/getfp.o: \$(STDLIB)/rt/+\$(ARCH)/getfp.s
@printf 'AS \t\$@\n'
@mkdir -p \$($cache)/rt
@@ -786,7 +794,13 @@ linux_vdso() {
gensrcs_math() {
gen_srcs math \
- math.ha floats.ha ints.ha uints.ha trig.ha \
+ math.ha \
+ fenv_func.ha \
+ 'fenv+$(ARCH).ha' \
+ floats.ha \
+ ints.ha \
+ uints.ha \
+ trig.ha \
$*
}
diff --git a/stdlib.mk b/stdlib.mk
@@ -18,6 +18,7 @@ stdlib_rt_linux_srcs= \
$(STDLIB)/rt/+linux/socket.ha \
$(STDLIB)/rt/+$(ARCH)/jmp.ha \
$(STDLIB)/rt/+$(ARCH)/backtrace.ha \
+ $(STDLIB)/rt/fenv_defs.ha \
$(STDLIB)/rt/ensure.ha \
$(STDLIB)/rt/jmp.ha \
$(STDLIB)/rt/malloc.ha \
@@ -42,6 +43,7 @@ stdlib_rt_freebsd_srcs= \
$(STDLIB)/rt/+freebsd/types.ha \
$(STDLIB)/rt/+$(ARCH)/jmp.ha \
$(STDLIB)/rt/+$(ARCH)/backtrace.ha \
+ $(STDLIB)/rt/fenv_defs.ha \
$(STDLIB)/rt/ensure.ha \
$(STDLIB)/rt/jmp.ha \
$(STDLIB)/rt/malloc.ha \
@@ -74,6 +76,7 @@ stdlib_asm=$(HARECACHE)/rt/syscall.o \
$(HARECACHE)/rt/longjmp.o \
$(HARECACHE)/rt/restore.o \
$(HARECACHE)/rt/getfp.o \
+ $(HARECACHE)/rt/fenv.o \
$(HARECACHE)/rt/start.o
$(HARECACHE)/rt/syscall.o: $(STDLIB)/rt/+$(PLATFORM)/syscall+$(ARCH).s
@@ -96,6 +99,11 @@ $(HARECACHE)/rt/restore.o: $(STDLIB)/rt/+$(ARCH)/restore.s
@mkdir -p $(HARECACHE)/rt
@$(AS) -o $@ $<
+$(HARECACHE)/rt/fenv.o: $(STDLIB)/rt/+$(ARCH)/fenv.s
+ @printf 'AS \t$@\n'
+ @mkdir -p $(HARECACHE)/rt
+ @$(AS) -o $@ $<
+
$(HARECACHE)/rt/getfp.o: $(STDLIB)/rt/+$(ARCH)/getfp.s
@printf 'AS \t$@\n'
@mkdir -p $(HARECACHE)/rt
@@ -1307,6 +1315,8 @@ $(HARECACHE)/linux/vdso/linux_vdso-linux.ssa: $(stdlib_linux_vdso_linux_srcs) $(
# math (+any)
stdlib_math_any_srcs= \
$(STDLIB)/math/math.ha \
+ $(STDLIB)/math/fenv_func.ha \
+ $(STDLIB)/math/fenv+$(ARCH).ha \
$(STDLIB)/math/floats.ha \
$(STDLIB)/math/ints.ha \
$(STDLIB)/math/uints.ha \
@@ -1826,6 +1836,7 @@ testlib_rt_linux_srcs= \
$(STDLIB)/rt/+linux/socket.ha \
$(STDLIB)/rt/+$(ARCH)/jmp.ha \
$(STDLIB)/rt/+$(ARCH)/backtrace.ha \
+ $(STDLIB)/rt/fenv_defs.ha \
$(STDLIB)/rt/ensure.ha \
$(STDLIB)/rt/jmp.ha \
$(STDLIB)/rt/malloc.ha \
@@ -1854,6 +1865,7 @@ testlib_rt_freebsd_srcs= \
$(STDLIB)/rt/+freebsd/types.ha \
$(STDLIB)/rt/+$(ARCH)/jmp.ha \
$(STDLIB)/rt/+$(ARCH)/backtrace.ha \
+ $(STDLIB)/rt/fenv_defs.ha \
$(STDLIB)/rt/ensure.ha \
$(STDLIB)/rt/jmp.ha \
$(STDLIB)/rt/malloc.ha \
@@ -1890,6 +1902,7 @@ testlib_asm=$(TESTCACHE)/rt/syscall.o \
$(TESTCACHE)/rt/longjmp.o \
$(TESTCACHE)/rt/restore.o \
$(TESTCACHE)/rt/getfp.o \
+ $(TESTCACHE)/rt/fenv.o \
$(TESTCACHE)/rt/start.o
$(TESTCACHE)/rt/syscall.o: $(STDLIB)/rt/+$(PLATFORM)/syscall+$(ARCH).s
@@ -1912,6 +1925,11 @@ $(TESTCACHE)/rt/restore.o: $(STDLIB)/rt/+$(ARCH)/restore.s
@mkdir -p $(TESTCACHE)/rt
@$(AS) -o $@ $<
+$(TESTCACHE)/rt/fenv.o: $(STDLIB)/rt/+$(ARCH)/fenv.s
+ @printf 'AS \t$@\n'
+ @mkdir -p $(TESTCACHE)/rt
+ @$(AS) -o $@ $<
+
$(TESTCACHE)/rt/getfp.o: $(STDLIB)/rt/+$(ARCH)/getfp.s
@printf 'AS \t$@\n'
@mkdir -p $(TESTCACHE)/rt
@@ -3158,6 +3176,8 @@ $(TESTCACHE)/linux/vdso/linux_vdso-linux.ssa: $(testlib_linux_vdso_linux_srcs) $
# math (+any)
testlib_math_any_srcs= \
$(STDLIB)/math/math.ha \
+ $(STDLIB)/math/fenv_func.ha \
+ $(STDLIB)/math/fenv+$(ARCH).ha \
$(STDLIB)/math/floats.ha \
$(STDLIB)/math/ints.ha \
$(STDLIB)/math/uints.ha \