| ... | @@ -4,6 +4,7 @@ const std = @import("std"); | ... | @@ -4,6 +4,7 @@ const std = @import("std"); |
| 4 | const math = std.math; | 4 | const math = std.math; |
| 5 | const expect = std.testing.expect; | 5 | const expect = std.testing.expect; |
| 6 | const expectEqual = std.testing.expectEqual; | 6 | const expectEqual = std.testing.expectEqual; |
| | 7 | const expectApproxEqAbs = std.testing.expectApproxEqAbs; |
| 7 | const expectApproxEqRel = std.testing.expectApproxEqRel; | 8 | const expectApproxEqRel = std.testing.expectApproxEqRel; |
| 8 | | 9 | |
| 9 | const symbol = @import("../c.zig").symbol; | 10 | const symbol = @import("../c.zig").symbol; |
| ... | @@ -37,6 +38,7 @@ comptime { | ... | @@ -37,6 +38,7 @@ comptime { |
| 37 | symbol(&coshf, "coshf"); | 38 | symbol(&coshf, "coshf"); |
| 38 | symbol(&hypotf, "hypotf"); | 39 | symbol(&hypotf, "hypotf"); |
| 39 | symbol(&hypotl, "hypotl"); | 40 | symbol(&hypotl, "hypotl"); |
| | 41 | symbol(&modff, "modff"); |
| 40 | symbol(&nan, "nan"); | 42 | symbol(&nan, "nan"); |
| 41 | symbol(&nanf, "nanf"); | 43 | symbol(&nanf, "nanf"); |
| 42 | symbol(&nanl, "nanl"); | 44 | symbol(&nanl, "nanl"); |
| ... | @@ -166,66 +168,88 @@ fn isnanl(x: c_longdouble) callconv(.c) c_int { | ... | @@ -166,66 +168,88 @@ fn isnanl(x: c_longdouble) callconv(.c) c_int { |
| 166 | return if (math.isNan(x)) 1 else 0; | 168 | return if (math.isNan(x)) 1 else 0; |
| 167 | } | 169 | } |
| 168 | | 170 | |
| 169 | fn modf(x: f64, iptr: *f64) callconv(.c) f64 { | 171 | fn modfGeneric(comptime T: type, x: T, iptr: *T) T { |
| 170 | if (math.isNegativeInf(x)) { | 172 | if (math.isNegativeInf(x)) { |
| 171 | iptr.* = -math.inf(f64); | 173 | iptr.* = -math.inf(T); |
| 172 | return -0.0; | 174 | return -0.0; |
| 173 | } | 175 | } |
| 174 | | 176 | |
| 175 | if (math.isPositiveInf(x)) { | 177 | if (math.isPositiveInf(x)) { |
| 176 | iptr.* = math.inf(f64); | 178 | iptr.* = math.inf(T); |
| 177 | return 0.0; | 179 | return 0.0; |
| 178 | } | 180 | } |
| 179 | | 181 | |
| 180 | // Avoids raising the INVALID flag on qemu-riscv | 182 | // Avoids raising the INVALID flag on qemu-riscv |
| 181 | if (math.isNan(x)) { | 183 | if (math.isNan(x)) { |
| 182 | iptr.* = math.nan(f64); | 184 | iptr.* = math.nan(T); |
| 183 | return math.nan(f64); | 185 | return math.nan(T); |
| 184 | } | 186 | } |
| 185 | | 187 | |
| 186 | const r = math.modf(x); | 188 | const r = math.modf(x); |
| 187 | iptr.* = r.ipart; | 189 | iptr.* = r.ipart; |
| 188 | | 190 | |
| 189 | // If the result would be a negative zero, we must be explicit about | 191 | // If the result is a negative zero, we must be explicit about |
| 190 | // returning a negative zero. | 192 | // returning a negative zero. |
| 191 | return if (math.isNegativeZero(x) or (x < 0.0 and x == r.ipart)) -0.0 else r.fpart; | 193 | return if (math.isNegativeZero(x) or (x < 0.0 and x == r.ipart)) -0.0 else r.fpart; |
| 192 | } | 194 | } |
| 193 | | 195 | |
| 194 | test "modf" { | 196 | fn modf(x: f64, iptr: *f64) callconv(.c) f64 { |
| 195 | var int: f64 = undefined; | 197 | return modfGeneric(f64, x, iptr); |
| | 198 | } |
| | 199 | |
| | 200 | fn modff(x: f32, iptr: *f32) callconv(.c) f32 { |
| | 201 | return modfGeneric(f32, x, iptr); |
| | 202 | } |
| | 203 | |
| | 204 | fn testModf(comptime T: type) !void { |
| | 205 | // Choose the appropriate `modf` impl to test based on type |
| | 206 | const f = switch (T) { |
| | 207 | f64 => modf, |
| | 208 | f32 => modff, |
| | 209 | else => @compileError("modf not implemented for " ++ @typeName(T)), |
| | 210 | }; |
| | 211 | |
| | 212 | var int: T = undefined; |
| 196 | const iptr = &int; | 213 | const iptr = &int; |
| 197 | const eps_val = 1e-6; | 214 | const eps_val: comptime_float = @max(1e-6, math.floatEps(T)); |
| 198 | | 215 | |
| 199 | const normal_frac = modf(1234.5678, iptr); | 216 | const normal_frac = f(@as(T, 1234.567), iptr); |
| 200 | try expectApproxEqRel(0.5678, normal_frac, eps_val); | 217 | // Account for precision error |
| 201 | try expectApproxEqRel(1234.0, iptr.*, eps_val); | 218 | const expected = 1234.567 - @as(T, 1234); |
| | 219 | try expectApproxEqAbs(expected, normal_frac, eps_val); |
| | 220 | try expectApproxEqRel(@as(T, 1234.0), iptr.*, eps_val); |
| 202 | | 221 | |
| 203 | // When `x` is a NaN, NaN is returned and `*iptr` is set to NaN | 222 | // When `x` is a NaN, NaN is returned and `*iptr` is set to NaN |
| 204 | const nan_frac = modf(math.nan(f64), iptr); | 223 | const nan_frac = f(math.nan(T), iptr); |
| 205 | try expect(math.isNan(nan_frac)); | 224 | try expect(math.isNan(nan_frac)); |
| 206 | try expect(math.isNan(iptr.*)); | 225 | try expect(math.isNan(iptr.*)); |
| 207 | | 226 | |
| 208 | // When `x` is positive infinity, +0 is returned and `*iptr` is set to | 227 | // When `x` is positive infinity, +0 is returned and `*iptr` is set to |
| 209 | // positive infinity | 228 | // positive infinity |
| 210 | const pos_zero_frac = modf(math.inf(f64), iptr); | 229 | const pos_zero_frac = f(math.inf(T), iptr); |
| 211 | try expect(math.isPositiveZero(pos_zero_frac)); | 230 | try expect(math.isPositiveZero(pos_zero_frac)); |
| 212 | try expect(math.isPositiveInf(iptr.*)); | 231 | try expect(math.isPositiveInf(iptr.*)); |
| 213 | | 232 | |
| 214 | // When `x` is negative infinity, -0 is returned and `*iptr` is set to | 233 | // When `x` is negative infinity, -0 is returned and `*iptr` is set to |
| 215 | // negative infinity | 234 | // negative infinity |
| 216 | const neg_zero_frac = modf(-math.inf(f64), iptr); | 235 | const neg_zero_frac = f(-math.inf(T), iptr); |
| 217 | try expect(math.isNegativeZero(neg_zero_frac)); | 236 | try expect(math.isNegativeZero(neg_zero_frac)); |
| 218 | try expect(math.isNegativeInf(iptr.*)); | 237 | try expect(math.isNegativeInf(iptr.*)); |
| 219 | | 238 | |
| 220 | // Return -0 when `x` is a negative integer | 239 | // Return -0 when `x` is a negative integer |
| 221 | const nz_frac = modf(-1000.0, iptr); | 240 | const nz_frac = f(@as(T, -1000.0), iptr); |
| 222 | try expect(math.isNegativeZero(nz_frac)); | 241 | try expect(math.isNegativeZero(nz_frac)); |
| 223 | try expectEqual(-1000.0, iptr.*); | 242 | try expectEqual(@as(T, -1000.0), iptr.*); |
| 224 | | 243 | |
| 225 | // Return +0 when `x` is a positive integer | 244 | // Return +0 when `x` is a positive integer |
| 226 | const pz_frac = modf(1000.0, iptr); | 245 | const pz_frac = f(@as(T, 1000.0), iptr); |
| 227 | try expect(math.isPositiveZero(pz_frac)); | 246 | try expect(math.isPositiveZero(pz_frac)); |
| 228 | try expectEqual(1000.0, iptr.*); | 247 | try expectEqual(@as(T, 1000.0), iptr.*); |
| | 248 | } |
| | 249 | |
| | 250 | test "modf" { |
| | 251 | try testModf(f64); |
| | 252 | try testModf(f32); |
| 229 | } | 253 | } |
| 230 | | 254 | |
| 231 | fn nan(_: [*:0]const c_char) callconv(.c) f64 { | 255 | fn nan(_: [*:0]const c_char) callconv(.c) f64 { |