aboutsummaryrefslogtreecommitdiff
path: root/lib/std/math/modf.zig
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std/math/modf.zig')
-rw-r--r--lib/std/math/modf.zig210
1 files changed, 210 insertions, 0 deletions
diff --git a/lib/std/math/modf.zig b/lib/std/math/modf.zig
new file mode 100644
index 0000000000..92194d4c75
--- /dev/null
+++ b/lib/std/math/modf.zig
@@ -0,0 +1,210 @@
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
+//
+// https://git.musl-libc.org/cgit/musl/tree/src/math/modff.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/modf.c
+
+const std = @import("../std.zig");
+const math = std.math;
+const expect = std.testing.expect;
+const maxInt = std.math.maxInt;
+
+fn modf_result(comptime T: type) type {
+ return struct {
+ fpart: T,
+ ipart: T,
+ };
+}
+pub const modf32_result = modf_result(f32);
+pub const modf64_result = modf_result(f64);
+
+/// Returns the integer and fractional floating-point numbers that sum to x. The sign of each
+/// result is the same as the sign of x.
+///
+/// Special Cases:
+/// - modf(+-inf) = +-inf, nan
+/// - modf(nan) = nan, nan
+pub fn modf(x: var) modf_result(@typeOf(x)) {
+ const T = @typeOf(x);
+ return switch (T) {
+ f32 => modf32(x),
+ f64 => modf64(x),
+ else => @compileError("modf not implemented for " ++ @typeName(T)),
+ };
+}
+
+fn modf32(x: f32) modf32_result {
+ var result: modf32_result = undefined;
+
+ const u = @bitCast(u32, x);
+ const e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F;
+ const us = u & 0x80000000;
+
+ // TODO: Shouldn't need this.
+ if (math.isInf(x)) {
+ result.ipart = x;
+ result.fpart = math.nan(f32);
+ return result;
+ }
+
+ // no fractional part
+ if (e >= 23) {
+ result.ipart = x;
+ if (e == 0x80 and u << 9 != 0) { // nan
+ result.fpart = x;
+ } else {
+ result.fpart = @bitCast(f32, us);
+ }
+ return result;
+ }
+
+ // no integral part
+ if (e < 0) {
+ result.ipart = @bitCast(f32, us);
+ result.fpart = x;
+ return result;
+ }
+
+ const mask = u32(0x007FFFFF) >> @intCast(u5, e);
+ if (u & mask == 0) {
+ result.ipart = x;
+ result.fpart = @bitCast(f32, us);
+ return result;
+ }
+
+ const uf = @bitCast(f32, u & ~mask);
+ result.ipart = uf;
+ result.fpart = x - uf;
+ return result;
+}
+
+fn modf64(x: f64) modf64_result {
+ var result: modf64_result = undefined;
+
+ const u = @bitCast(u64, x);
+ const e = @intCast(i32, (u >> 52) & 0x7FF) - 0x3FF;
+ const us = u & (1 << 63);
+
+ if (math.isInf(x)) {
+ result.ipart = x;
+ result.fpart = math.nan(f64);
+ return result;
+ }
+
+ // no fractional part
+ if (e >= 52) {
+ result.ipart = x;
+ if (e == 0x400 and u << 12 != 0) { // nan
+ result.fpart = x;
+ } else {
+ result.fpart = @bitCast(f64, us);
+ }
+ return result;
+ }
+
+ // no integral part
+ if (e < 0) {
+ result.ipart = @bitCast(f64, us);
+ result.fpart = x;
+ return result;
+ }
+
+ const mask = u64(maxInt(u64) >> 12) >> @intCast(u6, e);
+ if (u & mask == 0) {
+ result.ipart = x;
+ result.fpart = @bitCast(f64, us);
+ return result;
+ }
+
+ const uf = @bitCast(f64, u & ~mask);
+ result.ipart = uf;
+ result.fpart = x - uf;
+ return result;
+}
+
+test "math.modf" {
+ const a = modf(f32(1.0));
+ const b = modf32(1.0);
+ // NOTE: No struct comparison on generic return type function? non-named, makes sense, but still.
+ expect(a.ipart == b.ipart and a.fpart == b.fpart);
+
+ const c = modf(f64(1.0));
+ const d = modf64(1.0);
+ expect(a.ipart == b.ipart and a.fpart == b.fpart);
+}
+
+test "math.modf32" {
+ const epsilon = 0.000001;
+ var r: modf32_result = undefined;
+
+ r = modf32(1.0);
+ expect(math.approxEq(f32, r.ipart, 1.0, epsilon));
+ expect(math.approxEq(f32, r.fpart, 0.0, epsilon));
+
+ r = modf32(2.545);
+ expect(math.approxEq(f32, r.ipart, 2.0, epsilon));
+ expect(math.approxEq(f32, r.fpart, 0.545, epsilon));
+
+ r = modf32(3.978123);
+ expect(math.approxEq(f32, r.ipart, 3.0, epsilon));
+ expect(math.approxEq(f32, r.fpart, 0.978123, epsilon));
+
+ r = modf32(43874.3);
+ expect(math.approxEq(f32, r.ipart, 43874, epsilon));
+ expect(math.approxEq(f32, r.fpart, 0.300781, epsilon));
+
+ r = modf32(1234.340780);
+ expect(math.approxEq(f32, r.ipart, 1234, epsilon));
+ expect(math.approxEq(f32, r.fpart, 0.340820, epsilon));
+}
+
+test "math.modf64" {
+ const epsilon = 0.000001;
+ var r: modf64_result = undefined;
+
+ r = modf64(1.0);
+ expect(math.approxEq(f64, r.ipart, 1.0, epsilon));
+ expect(math.approxEq(f64, r.fpart, 0.0, epsilon));
+
+ r = modf64(2.545);
+ expect(math.approxEq(f64, r.ipart, 2.0, epsilon));
+ expect(math.approxEq(f64, r.fpart, 0.545, epsilon));
+
+ r = modf64(3.978123);
+ expect(math.approxEq(f64, r.ipart, 3.0, epsilon));
+ expect(math.approxEq(f64, r.fpart, 0.978123, epsilon));
+
+ r = modf64(43874.3);
+ expect(math.approxEq(f64, r.ipart, 43874, epsilon));
+ expect(math.approxEq(f64, r.fpart, 0.3, epsilon));
+
+ r = modf64(1234.340780);
+ expect(math.approxEq(f64, r.ipart, 1234, epsilon));
+ expect(math.approxEq(f64, r.fpart, 0.340780, epsilon));
+}
+
+test "math.modf32.special" {
+ var r: modf32_result = undefined;
+
+ r = modf32(math.inf(f32));
+ expect(math.isPositiveInf(r.ipart) and math.isNan(r.fpart));
+
+ r = modf32(-math.inf(f32));
+ expect(math.isNegativeInf(r.ipart) and math.isNan(r.fpart));
+
+ r = modf32(math.nan(f32));
+ expect(math.isNan(r.ipart) and math.isNan(r.fpart));
+}
+
+test "math.modf64.special" {
+ var r: modf64_result = undefined;
+
+ r = modf64(math.inf(f64));
+ expect(math.isPositiveInf(r.ipart) and math.isNan(r.fpart));
+
+ r = modf64(-math.inf(f64));
+ expect(math.isNegativeInf(r.ipart) and math.isNan(r.fpart));
+
+ r = modf64(math.nan(f64));
+ expect(math.isNan(r.ipart) and math.isNan(r.fpart));
+}