| ... | ... | @@ -3,6 +3,7 @@ |
| 3 | 3 | //! |
| 4 | 4 | //! https://git.musl-libc.org/cgit/musl/tree/src/math/tanf.c |
| 5 | 5 | //! https://git.musl-libc.org/cgit/musl/tree/src/math/tan.c |
| 6 | //! https://git.musl-libc.org/cgit/musl/tree/src/math/tanl.c |
| 6 | 7 | //! https://golang.org/src/math/tan.go |
| 7 | 8 | |
| 8 | 9 | const std = @import("std"); |
| ... | ... | @@ -10,10 +11,13 @@ const builtin = @import("builtin"); |
| 10 | 11 | const math = std.math; |
| 11 | 12 | const mem = std.mem; |
| 12 | 13 | const expect = std.testing.expect; |
| 14 | const expectApproxEqAbs = std.testing.expectApproxEqAbs; |
| 13 | 15 | |
| 14 | 16 | const kernel = @import("trig.zig"); |
| 15 | 17 | const rem_pio2 = @import("rem_pio2.zig").rem_pio2; |
| 16 | 18 | const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; |
| 19 | const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l; |
| 20 | const utils = @import("math_utils.zig"); |
| 17 | 21 | |
| 18 | 22 | const arch = builtin.cpu.arch; |
| 19 | 23 | const compiler_rt = @import("../compiler_rt.zig"); |
| ... | ... | @@ -116,14 +120,38 @@ pub fn tan(x: f64) callconv(.c) f64 { |
| 116 | 120 | return kernel.__tan(y[0], y[1], n & 1 != 0); |
| 117 | 121 | } |
| 118 | 122 | |
| 123 | fn tanlGeneric(comptime T: type, x: T) T { |
| 124 | if (!(T == f80 or T == f128)) { |
| 125 | @compileError("`tanlGeneric` implemented only for `f80` and `f128`, got: " ++ T); |
| 126 | } |
| 127 | |
| 128 | const se = utils.ldSignExponent(x) & 0x7fff; |
| 129 | if (se == 0x7fff) { |
| 130 | return x - x; |
| 131 | } |
| 132 | |
| 133 | const pi_4 = 0.78539816339744830962; |
| 134 | if (@abs(x) < pi_4) { |
| 135 | if (se < 0x3fff - math.floatMantissaBits(T) / 2) { |
| 136 | if (compiler_rt.want_float_exceptions) { |
| 137 | mem.doNotOptimizeAway(if (se == 0) x * 0x1p-120 else x + 0x1p120); |
| 138 | } |
| 139 | return x; |
| 140 | } |
| 141 | return kernel.__tanl(T, x, 0.0, 0); |
| 142 | } |
| 143 | |
| 144 | var y: [2]T = undefined; |
| 145 | const n = rem_pio2l(T, x, &y); |
| 146 | return kernel.__tanl(T, y[0], y[1], n & 1); |
| 147 | } |
| 148 | |
| 119 | 149 | pub fn __tanx(x: f80) callconv(.c) f80 { |
| 120 | | // TODO: more efficient implementation |
| 121 | | return @floatCast(tanq(x)); |
| 150 | return tanlGeneric(f80, x); |
| 122 | 151 | } |
| 123 | 152 | |
| 124 | 153 | pub fn tanq(x: f128) callconv(.c) f128 { |
| 125 | | // TODO: more correct implementation |
| 126 | | return tan(@floatCast(x)); |
| 154 | return tanlGeneric(f128, x); |
| 127 | 155 | } |
| 128 | 156 | |
| 129 | 157 | pub fn tanl(x: c_longdouble) callconv(.c) c_longdouble { |
| ... | ... | @@ -137,45 +165,80 @@ pub fn tanl(x: c_longdouble) callconv(.c) c_longdouble { |
| 137 | 165 | } |
| 138 | 166 | } |
| 139 | 167 | |
| 140 | | test "tan" { |
| 141 | | try expect(tan(@as(f32, 0.0)) == tanf(0.0)); |
| 142 | | try expect(tan(@as(f64, 0.0)) == tan(0.0)); |
| 168 | fn testTanNormal(comptime T: type) !void { |
| 169 | const f = switch (T) { |
| 170 | f32 => tanf, |
| 171 | f64 => tan, |
| 172 | else => @compileError("unimplemented"), |
| 173 | }; |
| 174 | const epsilon = 0.00001; |
| 175 | |
| 176 | try expectApproxEqAbs(@as(T, 0.0), f(0.0), epsilon); |
| 177 | try expectApproxEqAbs(@as(T, 0.202710), f(0.2), epsilon); |
| 178 | try expectApproxEqAbs(@as(T, 1.240422), f(0.8923), epsilon); |
| 179 | try expectApproxEqAbs(@as(T, 14.101420), f(1.5), epsilon); |
| 180 | try expectApproxEqAbs(@as(T, -0.254397), f(37.45), epsilon); |
| 181 | try expectApproxEqAbs(@as(T, 2.285837), f(89.123), epsilon); |
| 143 | 182 | } |
| 144 | 183 | |
| 145 | | test "tan32" { |
| 146 | | const epsilon = 0.00001; |
| 184 | fn testTanSpecial(comptime T: type) !void { |
| 185 | const f = switch (T) { |
| 186 | f32 => tanf, |
| 187 | f64 => tan, |
| 188 | f80 => __tanx, |
| 189 | f128 => tanq, |
| 190 | else => @compileError("unimplemented"), |
| 191 | }; |
| 192 | |
| 193 | try expect(math.isPositiveZero(f(0.0))); |
| 194 | try expect(math.isNegativeZero(f(-0.0))); |
| 195 | try expect(math.isNan(f(math.inf(f32)))); |
| 196 | try expect(math.isNan(f(-math.inf(f32)))); |
| 197 | try expect(math.isNan(f(math.nan(f32)))); |
| 198 | } |
| 147 | 199 | |
| 148 | | try expect(math.approxEqAbs(f32, tanf(0.0), 0.0, epsilon)); |
| 149 | | try expect(math.approxEqAbs(f32, tanf(0.2), 0.202710, epsilon)); |
| 150 | | try expect(math.approxEqAbs(f32, tanf(0.8923), 1.240422, epsilon)); |
| 151 | | try expect(math.approxEqAbs(f32, tanf(1.5), 14.101420, epsilon)); |
| 152 | | try expect(math.approxEqAbs(f32, tanf(37.45), -0.254397, epsilon)); |
| 153 | | try expect(math.approxEqAbs(f32, tanf(89.123), 2.285852, epsilon)); |
| 200 | test "tan32.normal" { |
| 201 | try testTanNormal(f32); |
| 154 | 202 | } |
| 155 | 203 | |
| 156 | | test "tan64" { |
| 157 | | const epsilon = 0.000001; |
| 204 | test "tan64.normal" { |
| 205 | try testTanNormal(f64); |
| 206 | } |
| 207 | |
| 208 | test "tan80.normal" { |
| 209 | const epsilon = math.floatEps(f80); |
| 158 | 210 | |
| 159 | | try expect(math.approxEqAbs(f64, tan(0.0), 0.0, epsilon)); |
| 160 | | try expect(math.approxEqAbs(f64, tan(0.2), 0.202710, epsilon)); |
| 161 | | try expect(math.approxEqAbs(f64, tan(0.8923), 1.240422, epsilon)); |
| 162 | | try expect(math.approxEqAbs(f64, tan(1.5), 14.101420, epsilon)); |
| 163 | | try expect(math.approxEqAbs(f64, tan(37.45), -0.254397, epsilon)); |
| 164 | | try expect(math.approxEqAbs(f64, tan(89.123), 2.2858376, epsilon)); |
| 211 | try expectApproxEqAbs(@as(f80, 0.0), __tanx(0.0), epsilon); |
| 212 | try expectApproxEqAbs(@as(f80, 0.2027100355086724833213582716475345), __tanx(0.2), epsilon); |
| 213 | try expectApproxEqAbs(@as(f80, 1.2404217445497097995561220131857544), __tanx(0.8923), epsilon); |
| 214 | try expectApproxEqAbs(@as(f80, 14.10141994717171938764), __tanx(1.5), epsilon); |
| 215 | try expectApproxEqAbs(@as(f80, -0.25439607116885656232), __tanx(37.45), epsilon); |
| 216 | try expectApproxEqAbs(@as(f80, 2.2858376251355320963), __tanx(89.123), epsilon); |
| 217 | } |
| 218 | |
| 219 | test "tan128.normal" { |
| 220 | const epsilon = math.floatEps(f128); |
| 221 | |
| 222 | try expectApproxEqAbs(@as(f128, 0.0), tanq(0.0), epsilon); |
| 223 | try expectApproxEqAbs(@as(f128, 0.2027100355086724833213582716475345), tanq(0.2), epsilon); |
| 224 | try expectApproxEqAbs(@as(f128, 1.2404217445497097995561220131857544), tanq(0.8923), epsilon); |
| 225 | try expectApproxEqAbs(@as(f128, 14.101419947171719387646083651987755), tanq(1.5), epsilon); |
| 226 | try expectApproxEqAbs(@as(f128, -0.2543960711688565630469573224504774), tanq(37.45), epsilon); |
| 227 | try expectApproxEqAbs(@as(f128, 2.2858376251355321074066028114094292), tanq(89.123), epsilon); |
| 165 | 228 | } |
| 166 | 229 | |
| 167 | 230 | test "tan32.special" { |
| 168 | | try expect(tanf(0.0) == 0.0); |
| 169 | | try expect(tanf(-0.0) == -0.0); |
| 170 | | try expect(math.isNan(tanf(math.inf(f32)))); |
| 171 | | try expect(math.isNan(tanf(-math.inf(f32)))); |
| 172 | | try expect(math.isNan(tanf(math.nan(f32)))); |
| 231 | try testTanSpecial(f32); |
| 173 | 232 | } |
| 174 | 233 | |
| 175 | 234 | test "tan64.special" { |
| 176 | | try expect(tan(0.0) == 0.0); |
| 177 | | try expect(tan(-0.0) == -0.0); |
| 178 | | try expect(math.isNan(tan(math.inf(f64)))); |
| 179 | | try expect(math.isNan(tan(-math.inf(f64)))); |
| 180 | | try expect(math.isNan(tan(math.nan(f64)))); |
| 235 | try testTanSpecial(f64); |
| 236 | } |
| 237 | |
| 238 | test "tan80.special" { |
| 239 | try testTanSpecial(f80); |
| 240 | } |
| 241 | |
| 242 | test "tan128.special" { |
| 243 | try testTanSpecial(f128); |
| 181 | 244 | } |