| ... | @@ -3,9 +3,13 @@ const builtin = @import("builtin"); | ... | @@ -3,9 +3,13 @@ const builtin = @import("builtin"); |
| 3 | const arch = builtin.cpu.arch; | 3 | const arch = builtin.cpu.arch; |
| 4 | const math = std.math; | 4 | const math = std.math; |
| 5 | const mem = std.mem; | 5 | const mem = std.mem; |
| | 6 | const expect = std.testing.expect; |
| | 7 | const expectApproxEqAbs = std.testing.expectApproxEqAbs; |
| 6 | const trig = @import("trig.zig"); | 8 | const trig = @import("trig.zig"); |
| 7 | const rem_pio2 = @import("rem_pio2.zig").rem_pio2; | 9 | const rem_pio2 = @import("rem_pio2.zig").rem_pio2; |
| 8 | const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; | 10 | const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; |
| | 11 | const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l; |
| | 12 | const utils = @import("math_utils.zig"); |
| 9 | const compiler_rt = @import("../compiler_rt.zig"); | 13 | const compiler_rt = @import("../compiler_rt.zig"); |
| 10 | const symbol = compiler_rt.symbol; | 14 | const symbol = compiler_rt.symbol; |
| 11 | | 15 | |
| ... | @@ -188,50 +192,12 @@ pub fn sincos(x: f64, r_sin: *f64, r_cos: *f64) callconv(.c) void { | ... | @@ -188,50 +192,12 @@ pub fn sincos(x: f64, r_sin: *f64, r_cos: *f64) callconv(.c) void { |
| 188 | } | 192 | } |
| 189 | } | 193 | } |
| 190 | | 194 | |
| 191 | pub fn __sincosx(x: f80, r_sin: *f80, r_cos: *f80) callconv(.c) void { | 195 | fn sincoslGeneric(comptime T: type, x: T, r_sin: *T, r_cos: *T) void { |
| 192 | // TODO: more efficient implementation | 196 | if (T != f80 and T != f128) { |
| 193 | //return sincos_generic(f80, x, r_sin, r_cos); | 197 | @compileError("`sincoslGeneric` implemented only for `f80` and `f128`, got: " ++ @typeName(T)); |
| 194 | var big_sin: f128 = undefined; | | |
| 195 | var big_cos: f128 = undefined; | | |
| 196 | sincosq(x, &big_sin, &big_cos); | | |
| 197 | r_sin.* = @as(f80, @floatCast(big_sin)); | | |
| 198 | r_cos.* = @as(f80, @floatCast(big_cos)); | | |
| 199 | } | | |
| 200 | | | |
| 201 | pub fn sincosq(x: f128, r_sin: *f128, r_cos: *f128) callconv(.c) void { | | |
| 202 | // TODO: more correct implementation | | |
| 203 | //return sincos_generic(f128, x, r_sin, r_cos); | | |
| 204 | var small_sin: f64 = undefined; | | |
| 205 | var small_cos: f64 = undefined; | | |
| 206 | sincos(@as(f64, @floatCast(x)), &small_sin, &small_cos); | | |
| 207 | r_sin.* = small_sin; | | |
| 208 | r_cos.* = small_cos; | | |
| 209 | } | | |
| 210 | | | |
| 211 | pub fn sincosl(x: c_longdouble, r_sin: *c_longdouble, r_cos: *c_longdouble) callconv(.c) void { | | |
| 212 | switch (@typeInfo(c_longdouble).float.bits) { | | |
| 213 | 16 => return __sincosh(x, r_sin, r_cos), | | |
| 214 | 32 => return sincosf(x, r_sin, r_cos), | | |
| 215 | 64 => return sincos(x, r_sin, r_cos), | | |
| 216 | 80 => return __sincosx(x, r_sin, r_cos), | | |
| 217 | 128 => return sincosq(x, r_sin, r_cos), | | |
| 218 | else => @compileError("unreachable"), | | |
| 219 | } | 198 | } |
| 220 | } | | |
| 221 | | | |
| 222 | pub const rem_pio2_generic = @compileError("TODO"); | | |
| 223 | | | |
| 224 | /// Ported from musl sincosl.c. Needs the following dependencies to be complete: | | |
| 225 | /// * rem_pio2_generic ported from __rem_pio2l.c | | |
| 226 | /// * trig.sin_generic ported from __sinl.c | | |
| 227 | /// * trig.cos_generic ported from __cosl.c | | |
| 228 | inline fn sincos_generic(comptime F: type, x: F, r_sin: *F, r_cos: *F) void { | | |
| 229 | const sc1pio4: F = 1.0 * math.pi / 4.0; | | |
| 230 | const bits = @typeInfo(F).float.bits; | | |
| 231 | const I = std.meta.Int(.unsigned, bits); | | |
| 232 | const ix = @as(I, @bitCast(x)) & (math.maxInt(I) >> 1); | | |
| 233 | const se: u16 = @truncate(ix >> (bits - 16)); | | |
| 234 | | 199 | |
| | 200 | const se = utils.ldSignExponent(x) & 0x7fff; |
| 235 | if (se == 0x7fff) { | 201 | if (se == 0x7fff) { |
| 236 | const result = x - x; | 202 | const result = x - x; |
| 237 | r_sin.* = result; | 203 | r_sin.* = result; |
| ... | @@ -239,26 +205,26 @@ inline fn sincos_generic(comptime F: type, x: F, r_sin: *F, r_cos: *F) void { | ... | @@ -239,26 +205,26 @@ inline fn sincos_generic(comptime F: type, x: F, r_sin: *F, r_cos: *F) void { |
| 239 | return; | 205 | return; |
| 240 | } | 206 | } |
| 241 | | 207 | |
| 242 | if (@as(F, @bitCast(ix)) < sc1pio4) { | 208 | if (@abs(x) < utils.pi_4) { |
| 243 | if (se < 0x3fff - math.floatFractionalBits(F) - 1) { | 209 | if (se < 0x3fff - math.floatMantissaBits(T)) { |
| 244 | // raise underflow if subnormal | 210 | // raise underflow if subnormal |
| 245 | if (se == 0) { | 211 | if (compiler_rt.want_float_exceptions and se == 0) { |
| 246 | if (compiler_rt.want_float_exceptions) mem.doNotOptimizeAway(x * 0x1p-120); | 212 | mem.doNotOptimizeAway(x * 0x1p-120); |
| 247 | } | 213 | } |
| 248 | r_sin.* = x; | 214 | r_sin.* = x; |
| 249 | // raise inexact if x!=0 | 215 | // raise inexact if x!=0 |
| 250 | r_cos.* = 1.0 + x; | 216 | r_cos.* = 1.0 + x; |
| 251 | return; | 217 | return; |
| 252 | } | 218 | } |
| 253 | r_sin.* = trig.sin_generic(F, x, 0, 0); | 219 | r_sin.* = trig.__sinl(T, x, 0.0, 0); |
| 254 | r_cos.* = trig.cos_generic(F, x, 0); | 220 | r_cos.* = trig.__cosl(T, x, 0.0); |
| 255 | return; | 221 | return; |
| 256 | } | 222 | } |
| 257 | | 223 | |
| 258 | var y: [2]F = undefined; | 224 | var y: [2]T = undefined; |
| 259 | const n = rem_pio2_generic(F, x, &y); | 225 | const n = rem_pio2l(T, x, &y); |
| 260 | const s = trig.sin_generic(F, y[0], y[1], 1); | 226 | const s = trig.__sinl(T, y[0], y[1], 1); |
| 261 | const c = trig.cos_generic(F, y[0], y[1]); | 227 | const c = trig.__cosl(T, y[0], y[1]); |
| 262 | switch (n & 3) { | 228 | switch (n & 3) { |
| 263 | 0 => { | 229 | 0 => { |
| 264 | r_sin.* = s; | 230 | r_sin.* = s; |
| ... | @@ -278,3 +244,207 @@ inline fn sincos_generic(comptime F: type, x: F, r_sin: *F, r_cos: *F) void { | ... | @@ -278,3 +244,207 @@ inline fn sincos_generic(comptime F: type, x: F, r_sin: *F, r_cos: *F) void { |
| 278 | }, | 244 | }, |
| 279 | } | 245 | } |
| 280 | } | 246 | } |
| | 247 | |
| | 248 | pub fn __sincosx(x: f80, r_sin: *f80, r_cos: *f80) callconv(.c) void { |
| | 249 | return sincoslGeneric(f80, x, r_sin, r_cos); |
| | 250 | } |
| | 251 | |
| | 252 | pub fn sincosq(x: f128, r_sin: *f128, r_cos: *f128) callconv(.c) void { |
| | 253 | return sincoslGeneric(f128, x, r_sin, r_cos); |
| | 254 | } |
| | 255 | |
| | 256 | pub fn sincosl(x: c_longdouble, r_sin: *c_longdouble, r_cos: *c_longdouble) callconv(.c) void { |
| | 257 | switch (@typeInfo(c_longdouble).float.bits) { |
| | 258 | 16 => return __sincosh(x, r_sin, r_cos), |
| | 259 | 32 => return sincosf(x, r_sin, r_cos), |
| | 260 | 64 => return sincos(x, r_sin, r_cos), |
| | 261 | 80 => return __sincosx(x, r_sin, r_cos), |
| | 262 | 128 => return sincosq(x, r_sin, r_cos), |
| | 263 | else => @compileError("unreachable"), |
| | 264 | } |
| | 265 | } |
| | 266 | |
| | 267 | fn testSincosSpecial(comptime T: type) !void { |
| | 268 | const f = switch (T) { |
| | 269 | f32 => sincosf, |
| | 270 | f64 => sincos, |
| | 271 | f80 => __sincosx, |
| | 272 | f128 => sincosq, |
| | 273 | else => @compileError("unimplemented"), |
| | 274 | }; |
| | 275 | |
| | 276 | var s: T = undefined; |
| | 277 | var c: T = undefined; |
| | 278 | |
| | 279 | f(0.0, &s, &c); |
| | 280 | try expect(math.isPositiveZero(s)); |
| | 281 | try expect(c == 1.0); |
| | 282 | |
| | 283 | f(-0.0, &s, &c); |
| | 284 | try expect(math.isNegativeZero(s)); |
| | 285 | try expect(c == 1.0); |
| | 286 | |
| | 287 | f(math.inf(T), &s, &c); |
| | 288 | try expect(math.isNan(s)); |
| | 289 | try expect(math.isNan(c)); |
| | 290 | |
| | 291 | f(-math.inf(T), &s, &c); |
| | 292 | try expect(math.isNan(s)); |
| | 293 | try expect(math.isNan(c)); |
| | 294 | |
| | 295 | f(math.nan(T), &s, &c); |
| | 296 | try expect(math.isNan(s)); |
| | 297 | try expect(math.isNan(c)); |
| | 298 | } |
| | 299 | |
| | 300 | test "sincos32.normal" { |
| | 301 | const epsilon = math.floatEps(f32); |
| | 302 | var s: f32 = undefined; |
| | 303 | var c: f32 = undefined; |
| | 304 | |
| | 305 | sincosf(0.0, &s, &c); |
| | 306 | try expectApproxEqAbs(@as(f32, 0.0), s, epsilon); |
| | 307 | try expectApproxEqAbs(@as(f32, 1.0), c, epsilon); |
| | 308 | |
| | 309 | sincosf(0.2, &s, &c); |
| | 310 | try expectApproxEqAbs(@as(f32, 0.19866933), s, epsilon); |
| | 311 | try expectApproxEqAbs(@as(f32, 0.9800666), c, epsilon); |
| | 312 | |
| | 313 | sincosf(0.8923, &s, &c); |
| | 314 | try expectApproxEqAbs(@as(f32, 0.77851737), s, epsilon); |
| | 315 | try expectApproxEqAbs(@as(f32, 0.6276231), c, epsilon); |
| | 316 | |
| | 317 | sincosf(1.5, &s, &c); |
| | 318 | try expectApproxEqAbs(@as(f32, 0.997495), s, epsilon); |
| | 319 | try expectApproxEqAbs(@as(f32, 0.0707372), c, epsilon); |
| | 320 | |
| | 321 | sincosf(-1.5, &s, &c); |
| | 322 | try expectApproxEqAbs(@as(f32, -0.997495), s, epsilon); |
| | 323 | try expectApproxEqAbs(@as(f32, 0.0707372), c, epsilon); |
| | 324 | |
| | 325 | sincosf(37.45, &s, &c); |
| | 326 | try expectApproxEqAbs(@as(f32, -0.24654257), s, epsilon); |
| | 327 | try expectApproxEqAbs(@as(f32, 0.96913195), c, epsilon); |
| | 328 | |
| | 329 | sincosf(89.123, &s, &c); |
| | 330 | try expectApproxEqAbs(@as(f32, 0.9161657), s, epsilon); |
| | 331 | try expectApproxEqAbs(@as(f32, 0.40079966), c, epsilon); |
| | 332 | } |
| | 333 | |
| | 334 | test "sincos32.special" { |
| | 335 | try testSincosSpecial(f32); |
| | 336 | } |
| | 337 | |
| | 338 | test "sincos64.normal" { |
| | 339 | const epsilon = math.floatEps(f64); |
| | 340 | var s: f64 = undefined; |
| | 341 | var c: f64 = undefined; |
| | 342 | |
| | 343 | sincos(0.0, &s, &c); |
| | 344 | try expectApproxEqAbs(@as(f64, 0.0), s, epsilon); |
| | 345 | try expectApproxEqAbs(@as(f64, 1.0), c, epsilon); |
| | 346 | |
| | 347 | sincos(0.2, &s, &c); |
| | 348 | try expectApproxEqAbs(@as(f64, 0.19866933079506122), s, epsilon); |
| | 349 | try expectApproxEqAbs(@as(f64, 0.9800665778412416), c, epsilon); |
| | 350 | |
| | 351 | sincos(0.8923, &s, &c); |
| | 352 | try expectApproxEqAbs(@as(f64, 0.7785173385577349), s, epsilon); |
| | 353 | try expectApproxEqAbs(@as(f64, 0.6276230983360804), c, epsilon); |
| | 354 | |
| | 355 | sincos(1.5, &s, &c); |
| | 356 | try expectApproxEqAbs(@as(f64, 0.9974949866040544), s, epsilon); |
| | 357 | try expectApproxEqAbs(@as(f64, 0.0707372016677029), c, epsilon); |
| | 358 | |
| | 359 | sincos(-1.5, &s, &c); |
| | 360 | try expectApproxEqAbs(@as(f64, -0.9974949866040544), s, epsilon); |
| | 361 | try expectApproxEqAbs(@as(f64, 0.0707372016677029), c, epsilon); |
| | 362 | |
| | 363 | sincos(37.45, &s, &c); |
| | 364 | try expectApproxEqAbs(@as(f64, -0.24654331551411082), s, epsilon); |
| | 365 | try expectApproxEqAbs(@as(f64, 0.9691317730707778), c, epsilon); |
| | 366 | |
| | 367 | sincos(89.123, &s, &c); |
| | 368 | try expectApproxEqAbs(@as(f64, 0.9161652766622714), s, epsilon); |
| | 369 | try expectApproxEqAbs(@as(f64, 0.4008006809354791), c, epsilon); |
| | 370 | } |
| | 371 | |
| | 372 | test "sincos64.special" { |
| | 373 | try testSincosSpecial(f64); |
| | 374 | } |
| | 375 | |
| | 376 | test "sincos80.normal" { |
| | 377 | const epsilon = math.floatEps(f80); |
| | 378 | var s: f80 = undefined; |
| | 379 | var c: f80 = undefined; |
| | 380 | |
| | 381 | __sincosx(0.0, &s, &c); |
| | 382 | try expectApproxEqAbs(@as(f80, 0.0), s, epsilon); |
| | 383 | try expectApproxEqAbs(@as(f80, 1.0), c, epsilon); |
| | 384 | |
| | 385 | __sincosx(0.2, &s, &c); |
| | 386 | try expectApproxEqAbs(@as(f80, 0.19866933079506121545941262711838975), s, epsilon); |
| | 387 | try expectApproxEqAbs(@as(f80, 0.98006657784124163112419651674816888), c, epsilon); |
| | 388 | |
| | 389 | __sincosx(0.8923, &s, &c); |
| | 390 | try expectApproxEqAbs(@as(f80, 0.77851733855773487830689285621486050), s, epsilon); |
| | 391 | try expectApproxEqAbs(@as(f80, 0.62762309833608037003563995939286067), c, epsilon); |
| | 392 | |
| | 393 | __sincosx(1.5, &s, &c); |
| | 394 | try expectApproxEqAbs(@as(f80, 0.99749498660405443094172337114148732), s, epsilon); |
| | 395 | try expectApproxEqAbs(@as(f80, 0.070737201667702910088189851434268747), c, epsilon); |
| | 396 | |
| | 397 | __sincosx(-1.5, &s, &c); |
| | 398 | try expectApproxEqAbs(@as(f80, -0.99749498660405443094172337114148732), s, epsilon); |
| | 399 | try expectApproxEqAbs(@as(f80, 0.070737201667702910088189851434268747), c, epsilon); |
| | 400 | |
| | 401 | __sincosx(37.45, &s, &c); |
| | 402 | try expectApproxEqAbs(@as(f80, -0.24654331551411356504), s, epsilon); |
| | 403 | try expectApproxEqAbs(@as(f80, 0.9691317730707771246), c, epsilon); |
| | 404 | |
| | 405 | __sincosx(89.123, &s, &c); |
| | 406 | try expectApproxEqAbs(@as(f80, 0.91616527666226951006), s, epsilon); |
| | 407 | try expectApproxEqAbs(@as(f80, 0.4008006809354834001), c, epsilon); |
| | 408 | } |
| | 409 | |
| | 410 | test "sincos80.special" { |
| | 411 | try testSincosSpecial(f80); |
| | 412 | } |
| | 413 | |
| | 414 | test "sincos128.normal" { |
| | 415 | const epsilon = math.floatEps(f128); |
| | 416 | var s: f128 = undefined; |
| | 417 | var c: f128 = undefined; |
| | 418 | |
| | 419 | sincosq(0.0, &s, &c); |
| | 420 | try expectApproxEqAbs(@as(f128, 0.0), s, epsilon); |
| | 421 | try expectApproxEqAbs(@as(f128, 1.0), c, epsilon); |
| | 422 | |
| | 423 | sincosq(0.2, &s, &c); |
| | 424 | try expectApproxEqAbs(@as(f128, 0.19866933079506121545941262711838975), s, epsilon); |
| | 425 | try expectApproxEqAbs(@as(f128, 0.98006657784124163112419651674816888), c, epsilon); |
| | 426 | |
| | 427 | sincosq(0.8923, &s, &c); |
| | 428 | try expectApproxEqAbs(@as(f128, 0.77851733855773487830689285621486050), s, epsilon); |
| | 429 | try expectApproxEqAbs(@as(f128, 0.62762309833608037003563995939286067), c, epsilon); |
| | 430 | |
| | 431 | sincosq(1.5, &s, &c); |
| | 432 | try expectApproxEqAbs(@as(f128, 0.99749498660405443094172337114148732), s, epsilon); |
| | 433 | try expectApproxEqAbs(@as(f128, 0.070737201667702910088189851434268747), c, epsilon); |
| | 434 | |
| | 435 | sincosq(-1.5, &s, &c); |
| | 436 | try expectApproxEqAbs(@as(f128, -0.99749498660405443094172337114148732), s, epsilon); |
| | 437 | try expectApproxEqAbs(@as(f128, 0.070737201667702910088189851434268747), c, epsilon); |
| | 438 | |
| | 439 | sincosq(37.45, &s, &c); |
| | 440 | try expectApproxEqAbs(@as(f128, -0.24654331551411356571238581321661085), s, epsilon); |
| | 441 | try expectApproxEqAbs(@as(f128, 0.96913177307077712443149563847233230), c, epsilon); |
| | 442 | |
| | 443 | sincosq(89.123, &s, &c); |
| | 444 | try expectApproxEqAbs(@as(f128, 0.91616527666226951075019849560482170), s, epsilon); |
| | 445 | try expectApproxEqAbs(@as(f128, 0.40080068093548339848199454493704702), c, epsilon); |
| | 446 | } |
| | 447 | |
| | 448 | test "sincos128.special" { |
| | 449 | try testSincosSpecial(f128); |
| | 450 | } |