| ... | ... | @@ -3,9 +3,13 @@ const builtin = @import("builtin"); |
| 3 | 3 | const arch = builtin.cpu.arch; |
| 4 | 4 | const math = std.math; |
| 5 | 5 | const mem = std.mem; |
| 6 | const expect = std.testing.expect; |
| 7 | const expectApproxEqAbs = std.testing.expectApproxEqAbs; |
| 6 | 8 | const trig = @import("trig.zig"); |
| 7 | 9 | const rem_pio2 = @import("rem_pio2.zig").rem_pio2; |
| 8 | 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 | 13 | const compiler_rt = @import("../compiler_rt.zig"); |
| 10 | 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 | 192 | } |
| 189 | 193 | } |
| 190 | 194 | |
| 191 | | pub fn __sincosx(x: f80, r_sin: *f80, r_cos: *f80) callconv(.c) void { |
| 192 | | // TODO: more efficient implementation |
| 193 | | //return sincos_generic(f80, x, r_sin, r_cos); |
| 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"), |
| 195 | fn sincoslGeneric(comptime T: type, x: T, r_sin: *T, r_cos: *T) void { |
| 196 | if (T != f80 and T != f128) { |
| 197 | @compileError("`sincoslGeneric` implemented only for `f80` and `f128`, got: " ++ @typeName(T)); |
| 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 | 201 | if (se == 0x7fff) { |
| 236 | 202 | const result = x - x; |
| 237 | 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 | 205 | return; |
| 240 | 206 | } |
| 241 | 207 | |
| 242 | | if (@as(F, @bitCast(ix)) < sc1pio4) { |
| 243 | | if (se < 0x3fff - math.floatFractionalBits(F) - 1) { |
| 208 | if (@abs(x) < utils.pi_4) { |
| 209 | if (se < 0x3fff - math.floatMantissaBits(T)) { |
| 244 | 210 | // raise underflow if subnormal |
| 245 | | if (se == 0) { |
| 246 | | if (compiler_rt.want_float_exceptions) mem.doNotOptimizeAway(x * 0x1p-120); |
| 211 | if (compiler_rt.want_float_exceptions and se == 0) { |
| 212 | mem.doNotOptimizeAway(x * 0x1p-120); |
| 247 | 213 | } |
| 248 | 214 | r_sin.* = x; |
| 249 | 215 | // raise inexact if x!=0 |
| 250 | 216 | r_cos.* = 1.0 + x; |
| 251 | 217 | return; |
| 252 | 218 | } |
| 253 | | r_sin.* = trig.sin_generic(F, x, 0, 0); |
| 254 | | r_cos.* = trig.cos_generic(F, x, 0); |
| 219 | r_sin.* = trig.__sinl(T, x, 0.0, 0); |
| 220 | r_cos.* = trig.__cosl(T, x, 0.0); |
| 255 | 221 | return; |
| 256 | 222 | } |
| 257 | 223 | |
| 258 | | var y: [2]F = undefined; |
| 259 | | const n = rem_pio2_generic(F, x, &y); |
| 260 | | const s = trig.sin_generic(F, y[0], y[1], 1); |
| 261 | | const c = trig.cos_generic(F, y[0], y[1]); |
| 224 | var y: [2]T = undefined; |
| 225 | const n = rem_pio2l(T, x, &y); |
| 226 | const s = trig.__sinl(T, y[0], y[1], 1); |
| 227 | const c = trig.__cosl(T, y[0], y[1]); |
| 262 | 228 | switch (n & 3) { |
| 263 | 229 | 0 => { |
| 264 | 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 | 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 | } |