| author | |
| committer | |
| log | 2fe7b06f3df06b7a442ffc3d9b951c5d52a11a24 |
| tree | b94c0f9ede8dd1bd01148f9e70e2f7359a15b040 |
| parent | 6115cf22404467fd13d0290fc022d51d372d139a |
std: add f128 implementations of fma, frexp, and ilogb. Expose `fmal` in
zig's freestanding libc. This makes `@mulAdd` work correctly for f128.
Fixes a CI regression from yesterday, where I added a usage of f128
`@mulAdd` into the self-hosted compiler.6 files changed, 324 insertions(+), 37 deletions(-)
lib/std/math.zig+1-2| ... | ... | @@ -229,8 +229,7 @@ pub const floor = @import("math/floor.zig").floor; |
| 229 | 229 | pub const trunc = @import("math/trunc.zig").trunc; |
| 230 | 230 | pub const round = @import("math/round.zig").round; |
| 231 | 231 | pub const frexp = @import("math/frexp.zig").frexp; |
| 232 | pub const frexp32_result = @import("math/frexp.zig").frexp32_result; | |
| 233 | pub const frexp64_result = @import("math/frexp.zig").frexp64_result; | |
| 232 | pub const Frexp = @import("math/frexp.zig").Frexp; | |
| 234 | 233 | pub const modf = @import("math/modf.zig").modf; |
| 235 | 234 | pub const modf32_result = @import("math/modf.zig").modf32_result; |
| 236 | 235 | pub const modf64_result = @import("math/modf.zig").modf64_result; |
lib/std/math/fma.zig+165-4| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | // Ported from musl, which is licensed under the MIT license: | |
| 1 | // Ported from musl, which is MIT licensed: | |
| 2 | 2 | // https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT |
| 3 | 3 | // |
| 4 | // https://git.musl-libc.org/cgit/musl/tree/src/math/fmal.c | |
| 4 | 5 | // https://git.musl-libc.org/cgit/musl/tree/src/math/fmaf.c |
| 5 | 6 | // https://git.musl-libc.org/cgit/musl/tree/src/math/fma.c |
| 6 | 7 | |
| ... | ... | @@ -13,6 +14,7 @@ pub fn fma(comptime T: type, x: T, y: T, z: T) T { |
| 13 | 14 | return switch (T) { |
| 14 | 15 | f32 => fma32(x, y, z), |
| 15 | 16 | f64 => fma64(x, y, z), |
| 17 | f128 => fma128(x, y, z), | |
| 16 | 18 | else => @compileError("fma not implemented for " ++ @typeName(T)), |
| 17 | 19 | }; |
| 18 | 20 | } |
| ... | ... | @@ -142,12 +144,159 @@ fn add_and_denorm(a: f64, b: f64, scale: i32) f64 { |
| 142 | 144 | return math.scalbn(sum.hi, scale); |
| 143 | 145 | } |
| 144 | 146 | |
| 145 | test "math.fma" { | |
| 147 | /// A struct that represents a floating-point number with twice the precision | |
| 148 | /// of f128. We maintain the invariant that "hi" stores the high-order | |
| 149 | /// bits of the result. | |
| 150 | const dd128 = struct { | |
| 151 | hi: f128, | |
| 152 | lo: f128, | |
| 153 | }; | |
| 154 | ||
| 155 | /// Compute a+b exactly, returning the exact result in a struct dd. We assume | |
| 156 | /// that both a and b are finite, but make no assumptions about their relative | |
| 157 | /// magnitudes. | |
| 158 | fn dd_add128(a: f128, b: f128) dd128 { | |
| 159 | var ret: dd128 = undefined; | |
| 160 | ret.hi = a + b; | |
| 161 | const s = ret.hi - a; | |
| 162 | ret.lo = (a - (ret.hi - s)) + (b - s); | |
| 163 | return ret; | |
| 164 | } | |
| 165 | ||
| 166 | /// Compute a+b, with a small tweak: The least significant bit of the | |
| 167 | /// result is adjusted into a sticky bit summarizing all the bits that | |
| 168 | /// were lost to rounding. This adjustment negates the effects of double | |
| 169 | /// rounding when the result is added to another number with a higher | |
| 170 | /// exponent. For an explanation of round and sticky bits, see any reference | |
| 171 | /// on FPU design, e.g., | |
| 172 | /// | |
| 173 | /// J. Coonen. An Implementation Guide to a Proposed Standard for | |
| 174 | /// Floating-Point Arithmetic. Computer, vol. 13, no. 1, Jan 1980. | |
| 175 | fn add_adjusted128(a: f128, b: f128) f128 { | |
| 176 | var sum = dd_add128(a, b); | |
| 177 | if (sum.lo != 0) { | |
| 178 | var uhii = @bitCast(u128, sum.hi); | |
| 179 | if (uhii & 1 == 0) { | |
| 180 | // hibits += copysign(1.0, sum.hi, sum.lo) | |
| 181 | const uloi = @bitCast(u128, sum.lo); | |
| 182 | uhii += 1 - ((uhii ^ uloi) >> 126); | |
| 183 | sum.hi = @bitCast(f128, uhii); | |
| 184 | } | |
| 185 | } | |
| 186 | return sum.hi; | |
| 187 | } | |
| 188 | ||
| 189 | /// Compute ldexp(a+b, scale) with a single rounding error. It is assumed | |
| 190 | /// that the result will be subnormal, and care is taken to ensure that | |
| 191 | /// double rounding does not occur. | |
| 192 | fn add_and_denorm128(a: f128, b: f128, scale: i32) f128 { | |
| 193 | var sum = dd_add128(a, b); | |
| 194 | // If we are losing at least two bits of accuracy to denormalization, | |
| 195 | // then the first lost bit becomes a round bit, and we adjust the | |
| 196 | // lowest bit of sum.hi to make it a sticky bit summarizing all the | |
| 197 | // bits in sum.lo. With the sticky bit adjusted, the hardware will | |
| 198 | // break any ties in the correct direction. | |
| 199 | // | |
| 200 | // If we are losing only one bit to denormalization, however, we must | |
| 201 | // break the ties manually. | |
| 202 | if (sum.lo != 0) { | |
| 203 | var uhii = @bitCast(u128, sum.hi); | |
| 204 | const bits_lost = -@intCast(i32, (uhii >> 112) & 0x7FFF) - scale + 1; | |
| 205 | if ((bits_lost != 1) == (uhii & 1 != 0)) { | |
| 206 | const uloi = @bitCast(u128, sum.lo); | |
| 207 | uhii += 1 - (((uhii ^ uloi) >> 126) & 2); | |
| 208 | sum.hi = @bitCast(f128, uhii); | |
| 209 | } | |
| 210 | } | |
| 211 | return math.scalbn(sum.hi, scale); | |
| 212 | } | |
| 213 | ||
| 214 | /// Compute a*b exactly, returning the exact result in a struct dd. We assume | |
| 215 | /// that both a and b are normalized, so no underflow or overflow will occur. | |
| 216 | /// The current rounding mode must be round-to-nearest. | |
| 217 | fn dd_mul128(a: f128, b: f128) dd128 { | |
| 218 | var ret: dd128 = undefined; | |
| 219 | const split: f128 = 0x1.0p57 + 1.0; | |
| 220 | ||
| 221 | var p = a * split; | |
| 222 | var ha = a - p; | |
| 223 | ha += p; | |
| 224 | var la = a - ha; | |
| 225 | ||
| 226 | p = b * split; | |
| 227 | var hb = b - p; | |
| 228 | hb += p; | |
| 229 | var lb = b - hb; | |
| 230 | ||
| 231 | p = ha * hb; | |
| 232 | var q = ha * lb + la * hb; | |
| 233 | ||
| 234 | ret.hi = p + q; | |
| 235 | ret.lo = p - ret.hi + q + la * lb; | |
| 236 | return ret; | |
| 237 | } | |
| 238 | ||
| 239 | /// Fused multiply-add: Compute x * y + z with a single rounding error. | |
| 240 | /// | |
| 241 | /// We use scaling to avoid overflow/underflow, along with the | |
| 242 | /// canonical precision-doubling technique adapted from: | |
| 243 | /// | |
| 244 | /// Dekker, T. A Floating-Point Technique for Extending the | |
| 245 | /// Available Precision. Numer. Math. 18, 224-242 (1971). | |
| 246 | fn fma128(x: f128, y: f128, z: f128) f128 { | |
| 247 | if (!math.isFinite(x) or !math.isFinite(y)) { | |
| 248 | return x * y + z; | |
| 249 | } | |
| 250 | if (!math.isFinite(z)) { | |
| 251 | return z; | |
| 252 | } | |
| 253 | if (x == 0.0 or y == 0.0) { | |
| 254 | return x * y + z; | |
| 255 | } | |
| 256 | if (z == 0.0) { | |
| 257 | return x * y; | |
| 258 | } | |
| 259 | ||
| 260 | const x1 = math.frexp(x); | |
| 261 | var ex = x1.exponent; | |
| 262 | var xs = x1.significand; | |
| 263 | const x2 = math.frexp(y); | |
| 264 | var ey = x2.exponent; | |
| 265 | var ys = x2.significand; | |
| 266 | const x3 = math.frexp(z); | |
| 267 | var ez = x3.exponent; | |
| 268 | var zs = x3.significand; | |
| 269 | ||
| 270 | var spread = ex + ey - ez; | |
| 271 | if (spread <= 113 * 2) { | |
| 272 | zs = math.scalbn(zs, -spread); | |
| 273 | } else { | |
| 274 | zs = math.copysign(f128, math.f128_min, zs); | |
| 275 | } | |
| 276 | ||
| 277 | const xy = dd_mul128(xs, ys); | |
| 278 | const r = dd_add128(xy.hi, zs); | |
| 279 | spread = ex + ey; | |
| 280 | ||
| 281 | if (r.hi == 0.0) { | |
| 282 | return xy.hi + zs + math.scalbn(xy.lo, spread); | |
| 283 | } | |
| 284 | ||
| 285 | const adj = add_adjusted128(r.lo, xy.lo); | |
| 286 | if (spread + math.ilogb(r.hi) > -16383) { | |
| 287 | return math.scalbn(r.hi + adj, spread); | |
| 288 | } else { | |
| 289 | return add_and_denorm128(r.hi, adj, spread); | |
| 290 | } | |
| 291 | } | |
| 292 | ||
| 293 | test "type dispatch" { | |
| 146 | 294 | try expect(fma(f32, 0.0, 1.0, 1.0) == fma32(0.0, 1.0, 1.0)); |
| 147 | 295 | try expect(fma(f64, 0.0, 1.0, 1.0) == fma64(0.0, 1.0, 1.0)); |
| 296 | try expect(fma(f128, 0.0, 1.0, 1.0) == fma128(0.0, 1.0, 1.0)); | |
| 148 | 297 | } |
| 149 | 298 | |
| 150 | test "math.fma32" { | |
| 299 | test "32" { | |
| 151 | 300 | const epsilon = 0.000001; |
| 152 | 301 | |
| 153 | 302 | try expect(math.approxEqAbs(f32, fma32(0.0, 5.0, 9.124), 9.124, epsilon)); |
| ... | ... | @@ -159,7 +308,7 @@ test "math.fma32" { |
| 159 | 308 | try expect(math.approxEqAbs(f32, fma32(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); |
| 160 | 309 | } |
| 161 | 310 | |
| 162 | test "math.fma64" { | |
| 311 | test "64" { | |
| 163 | 312 | const epsilon = 0.000001; |
| 164 | 313 | |
| 165 | 314 | try expect(math.approxEqAbs(f64, fma64(0.0, 5.0, 9.124), 9.124, epsilon)); |
| ... | ... | @@ -170,3 +319,15 @@ test "math.fma64" { |
| 170 | 319 | try expect(math.approxEqAbs(f64, fma64(89.123, 5.0, 9.124), 454.739, epsilon)); |
| 171 | 320 | try expect(math.approxEqAbs(f64, fma64(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); |
| 172 | 321 | } |
| 322 | ||
| 323 | test "128" { | |
| 324 | const epsilon = 0.000001; | |
| 325 | ||
| 326 | try expect(math.approxEqAbs(f128, fma128(0.0, 5.0, 9.124), 9.124, epsilon)); | |
| 327 | try expect(math.approxEqAbs(f128, fma128(0.2, 5.0, 9.124), 10.124, epsilon)); | |
| 328 | try expect(math.approxEqAbs(f128, fma128(0.8923, 5.0, 9.124), 13.5855, epsilon)); | |
| 329 | try expect(math.approxEqAbs(f128, fma128(1.5, 5.0, 9.124), 16.624, epsilon)); | |
| 330 | try expect(math.approxEqAbs(f128, fma128(37.45, 5.0, 9.124), 196.374, epsilon)); | |
| 331 | try expect(math.approxEqAbs(f128, fma128(89.123, 5.0, 9.124), 454.739, epsilon)); | |
| 332 | try expect(math.approxEqAbs(f128, fma128(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); | |
| 333 | } |
lib/std/math/frexp.zig+91-18| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | // Ported from musl, which is licensed under the MIT license: | |
| 1 | // Ported from musl, which is MIT licensed: | |
| 2 | 2 | // https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT |
| 3 | 3 | // |
| 4 | // https://git.musl-libc.org/cgit/musl/tree/src/math/frexpl.c | |
| 4 | 5 | // https://git.musl-libc.org/cgit/musl/tree/src/math/frexpf.c |
| 5 | 6 | // https://git.musl-libc.org/cgit/musl/tree/src/math/frexp.c |
| 6 | 7 | |
| ... | ... | @@ -8,14 +9,12 @@ const std = @import("../std.zig"); |
| 8 | 9 | const math = std.math; |
| 9 | 10 | const expect = std.testing.expect; |
| 10 | 11 | |
| 11 | fn frexp_result(comptime T: type) type { | |
| 12 | pub fn Frexp(comptime T: type) type { | |
| 12 | 13 | return struct { |
| 13 | 14 | significand: T, |
| 14 | 15 | exponent: i32, |
| 15 | 16 | }; |
| 16 | 17 | } |
| 17 | pub const frexp32_result = frexp_result(f32); | |
| 18 | pub const frexp64_result = frexp_result(f64); | |
| 19 | 18 | |
| 20 | 19 | /// Breaks x into a normalized fraction and an integral power of two. |
| 21 | 20 | /// f == frac * 2^exp, with |frac| in the interval [0.5, 1). |
| ... | ... | @@ -24,17 +23,20 @@ pub const frexp64_result = frexp_result(f64); |
| 24 | 23 | /// - frexp(+-0) = +-0, 0 |
| 25 | 24 | /// - frexp(+-inf) = +-inf, 0 |
| 26 | 25 | /// - frexp(nan) = nan, undefined |
| 27 | pub fn frexp(x: anytype) frexp_result(@TypeOf(x)) { | |
| 26 | pub fn frexp(x: anytype) Frexp(@TypeOf(x)) { | |
| 28 | 27 | const T = @TypeOf(x); |
| 29 | 28 | return switch (T) { |
| 30 | 29 | f32 => frexp32(x), |
| 31 | 30 | f64 => frexp64(x), |
| 31 | f128 => frexp128(x), | |
| 32 | 32 | else => @compileError("frexp not implemented for " ++ @typeName(T)), |
| 33 | 33 | }; |
| 34 | 34 | } |
| 35 | 35 | |
| 36 | fn frexp32(x: f32) frexp32_result { | |
| 37 | var result: frexp32_result = undefined; | |
| 36 | // TODO: unify all these implementations using generics | |
| 37 | ||
| 38 | fn frexp32(x: f32) Frexp(f32) { | |
| 39 | var result: Frexp(f32) = undefined; | |
| 38 | 40 | |
| 39 | 41 | var y = @bitCast(u32, x); |
| 40 | 42 | const e = @intCast(i32, y >> 23) & 0xFF; |
| ... | ... | @@ -70,8 +72,8 @@ fn frexp32(x: f32) frexp32_result { |
| 70 | 72 | return result; |
| 71 | 73 | } |
| 72 | 74 | |
| 73 | fn frexp64(x: f64) frexp64_result { | |
| 74 | var result: frexp64_result = undefined; | |
| 75 | fn frexp64(x: f64) Frexp(f64) { | |
| 76 | var result: Frexp(f64) = undefined; | |
| 75 | 77 | |
| 76 | 78 | var y = @bitCast(u64, x); |
| 77 | 79 | const e = @intCast(i32, y >> 52) & 0x7FF; |
| ... | ... | @@ -107,7 +109,44 @@ fn frexp64(x: f64) frexp64_result { |
| 107 | 109 | return result; |
| 108 | 110 | } |
| 109 | 111 | |
| 110 | test "math.frexp" { | |
| 112 | fn frexp128(x: f128) Frexp(f128) { | |
| 113 | var result: Frexp(f128) = undefined; | |
| 114 | ||
| 115 | var y = @bitCast(u128, x); | |
| 116 | const e = @intCast(i32, y >> 112) & 0x7FFF; | |
| 117 | ||
| 118 | if (e == 0) { | |
| 119 | if (x != 0) { | |
| 120 | // subnormal | |
| 121 | result = frexp128(x * 0x1.0p120); | |
| 122 | result.exponent -= 120; | |
| 123 | } else { | |
| 124 | // frexp(+-0) = (+-0, 0) | |
| 125 | result.significand = x; | |
| 126 | result.exponent = 0; | |
| 127 | } | |
| 128 | return result; | |
| 129 | } else if (e == 0x7FFF) { | |
| 130 | // frexp(nan) = (nan, undefined) | |
| 131 | result.significand = x; | |
| 132 | result.exponent = undefined; | |
| 133 | ||
| 134 | // frexp(+-inf) = (+-inf, 0) | |
| 135 | if (math.isInf(x)) { | |
| 136 | result.exponent = 0; | |
| 137 | } | |
| 138 | ||
| 139 | return result; | |
| 140 | } | |
| 141 | ||
| 142 | result.exponent = e - 0x3FFE; | |
| 143 | y &= 0x8000FFFFFFFFFFFFFFFFFFFFFFFFFFFF; | |
| 144 | y |= 0x3FFE0000000000000000000000000000; | |
| 145 | result.significand = @bitCast(f128, y); | |
| 146 | return result; | |
| 147 | } | |
| 148 | ||
| 149 | test "type dispatch" { | |
| 111 | 150 | const a = frexp(@as(f32, 1.3)); |
| 112 | 151 | const b = frexp32(1.3); |
| 113 | 152 | try expect(a.significand == b.significand and a.exponent == b.exponent); |
| ... | ... | @@ -115,11 +154,15 @@ test "math.frexp" { |
| 115 | 154 | const c = frexp(@as(f64, 1.3)); |
| 116 | 155 | const d = frexp64(1.3); |
| 117 | 156 | try expect(c.significand == d.significand and c.exponent == d.exponent); |
| 157 | ||
| 158 | const e = frexp(@as(f128, 1.3)); | |
| 159 | const f = frexp128(1.3); | |
| 160 | try expect(e.significand == f.significand and e.exponent == f.exponent); | |
| 118 | 161 | } |
| 119 | 162 | |
| 120 | test "math.frexp32" { | |
| 163 | test "32" { | |
| 121 | 164 | const epsilon = 0.000001; |
| 122 | var r: frexp32_result = undefined; | |
| 165 | var r: Frexp(f32) = undefined; | |
| 123 | 166 | |
| 124 | 167 | r = frexp32(1.3); |
| 125 | 168 | try expect(math.approxEqAbs(f32, r.significand, 0.65, epsilon) and r.exponent == 1); |
| ... | ... | @@ -128,9 +171,9 @@ test "math.frexp32" { |
| 128 | 171 | try expect(math.approxEqAbs(f32, r.significand, 0.609558, epsilon) and r.exponent == 7); |
| 129 | 172 | } |
| 130 | 173 | |
| 131 | test "math.frexp64" { | |
| 174 | test "64" { | |
| 132 | 175 | const epsilon = 0.000001; |
| 133 | var r: frexp64_result = undefined; | |
| 176 | var r: Frexp(f64) = undefined; | |
| 134 | 177 | |
| 135 | 178 | r = frexp64(1.3); |
| 136 | 179 | try expect(math.approxEqAbs(f64, r.significand, 0.65, epsilon) and r.exponent == 1); |
| ... | ... | @@ -139,8 +182,19 @@ test "math.frexp64" { |
| 139 | 182 | try expect(math.approxEqAbs(f64, r.significand, 0.609558, epsilon) and r.exponent == 7); |
| 140 | 183 | } |
| 141 | 184 | |
| 142 | test "math.frexp32.special" { | |
| 143 | var r: frexp32_result = undefined; | |
| 185 | test "128" { | |
| 186 | const epsilon = 0.000001; | |
| 187 | var r: Frexp(f128) = undefined; | |
| 188 | ||
| 189 | r = frexp128(1.3); | |
| 190 | try expect(math.approxEqAbs(f128, r.significand, 0.65, epsilon) and r.exponent == 1); | |
| 191 | ||
| 192 | r = frexp128(78.0234); | |
| 193 | try expect(math.approxEqAbs(f128, r.significand, 0.609558, epsilon) and r.exponent == 7); | |
| 194 | } | |
| 195 | ||
| 196 | test "32 special" { | |
| 197 | var r: Frexp(f32) = undefined; | |
| 144 | 198 | |
| 145 | 199 | r = frexp32(0.0); |
| 146 | 200 | try expect(r.significand == 0.0 and r.exponent == 0); |
| ... | ... | @@ -158,8 +212,8 @@ test "math.frexp32.special" { |
| 158 | 212 | try expect(math.isNan(r.significand)); |
| 159 | 213 | } |
| 160 | 214 | |
| 161 | test "math.frexp64.special" { | |
| 162 | var r: frexp64_result = undefined; | |
| 215 | test "64 special" { | |
| 216 | var r: Frexp(f64) = undefined; | |
| 163 | 217 | |
| 164 | 218 | r = frexp64(0.0); |
| 165 | 219 | try expect(r.significand == 0.0 and r.exponent == 0); |
| ... | ... | @@ -176,3 +230,22 @@ test "math.frexp64.special" { |
| 176 | 230 | r = frexp64(math.nan(f64)); |
| 177 | 231 | try expect(math.isNan(r.significand)); |
| 178 | 232 | } |
| 233 | ||
| 234 | test "128 special" { | |
| 235 | var r: Frexp(f128) = undefined; | |
| 236 | ||
| 237 | r = frexp128(0.0); | |
| 238 | try expect(r.significand == 0.0 and r.exponent == 0); | |
| 239 | ||
| 240 | r = frexp128(-0.0); | |
| 241 | try expect(r.significand == -0.0 and r.exponent == 0); | |
| 242 | ||
| 243 | r = frexp128(math.inf(f128)); | |
| 244 | try expect(math.isPositiveInf(r.significand) and r.exponent == 0); | |
| 245 | ||
| 246 | r = frexp128(-math.inf(f128)); | |
| 247 | try expect(math.isNegativeInf(r.significand) and r.exponent == 0); | |
| 248 | ||
| 249 | r = frexp128(math.nan(f128)); | |
| 250 | try expect(math.isNan(r.significand)); | |
| 251 | } |
lib/std/math/ilogb.zig+57-6| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | // Ported from musl, which is licensed under the MIT license: | |
| 1 | // Ported from musl, which is MIT licensed. | |
| 2 | 2 | // https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT |
| 3 | 3 | // |
| 4 | // https://git.musl-libc.org/cgit/musl/tree/src/math/ilogbl.c | |
| 4 | 5 | // https://git.musl-libc.org/cgit/musl/tree/src/math/ilogbf.c |
| 5 | 6 | // https://git.musl-libc.org/cgit/musl/tree/src/math/ilogb.c |
| 6 | 7 | |
| ... | ... | @@ -21,10 +22,13 @@ pub fn ilogb(x: anytype) i32 { |
| 21 | 22 | return switch (T) { |
| 22 | 23 | f32 => ilogb32(x), |
| 23 | 24 | f64 => ilogb64(x), |
| 25 | f128 => ilogb128(x), | |
| 24 | 26 | else => @compileError("ilogb not implemented for " ++ @typeName(T)), |
| 25 | 27 | }; |
| 26 | 28 | } |
| 27 | 29 | |
| 30 | // TODO: unify these implementations with generics | |
| 31 | ||
| 28 | 32 | // NOTE: Should these be exposed publicly? |
| 29 | 33 | const fp_ilogbnan = -1 - @as(i32, maxInt(u32) >> 1); |
| 30 | 34 | const fp_ilogb0 = fp_ilogbnan; |
| ... | ... | @@ -100,12 +104,43 @@ fn ilogb64(x: f64) i32 { |
| 100 | 104 | return e - 0x3FF; |
| 101 | 105 | } |
| 102 | 106 | |
| 103 | test "math.ilogb" { | |
| 107 | fn ilogb128(x: f128) i32 { | |
| 108 | var u = @bitCast(u128, x); | |
| 109 | var e = @intCast(i32, (u >> 112) & 0x7FFF); | |
| 110 | ||
| 111 | if (math.isNan(x)) { | |
| 112 | return maxInt(i32); | |
| 113 | } | |
| 114 | ||
| 115 | if (e == 0) { | |
| 116 | u <<= 16; | |
| 117 | if (u == 0) { | |
| 118 | math.raiseInvalid(); | |
| 119 | return fp_ilogb0; | |
| 120 | } | |
| 121 | ||
| 122 | // subnormal x | |
| 123 | return ilogb128(x * 0x1p120) - 120; | |
| 124 | } | |
| 125 | ||
| 126 | if (e == 0x7FFF) { | |
| 127 | math.raiseInvalid(); | |
| 128 | if (u << 16 != 0) { | |
| 129 | return fp_ilogbnan; | |
| 130 | } else { | |
| 131 | return maxInt(i32); | |
| 132 | } | |
| 133 | } | |
| 134 | ||
| 135 | return e - 0x3FFF; | |
| 136 | } | |
| 137 | ||
| 138 | test "type dispatch" { | |
| 104 | 139 | try expect(ilogb(@as(f32, 0.2)) == ilogb32(0.2)); |
| 105 | 140 | try expect(ilogb(@as(f64, 0.2)) == ilogb64(0.2)); |
| 106 | 141 | } |
| 107 | 142 | |
| 108 | test "math.ilogb32" { | |
| 143 | test "32" { | |
| 109 | 144 | try expect(ilogb32(0.0) == fp_ilogb0); |
| 110 | 145 | try expect(ilogb32(0.5) == -1); |
| 111 | 146 | try expect(ilogb32(0.8923) == -1); |
| ... | ... | @@ -114,7 +149,7 @@ test "math.ilogb32" { |
| 114 | 149 | try expect(ilogb32(2398.23) == 11); |
| 115 | 150 | } |
| 116 | 151 | |
| 117 | test "math.ilogb64" { | |
| 152 | test "64" { | |
| 118 | 153 | try expect(ilogb64(0.0) == fp_ilogb0); |
| 119 | 154 | try expect(ilogb64(0.5) == -1); |
| 120 | 155 | try expect(ilogb64(0.8923) == -1); |
| ... | ... | @@ -123,16 +158,32 @@ test "math.ilogb64" { |
| 123 | 158 | try expect(ilogb64(2398.23) == 11); |
| 124 | 159 | } |
| 125 | 160 | |
| 126 | test "math.ilogb32.special" { | |
| 161 | test "128" { | |
| 162 | try expect(ilogb128(0.0) == fp_ilogb0); | |
| 163 | try expect(ilogb128(0.5) == -1); | |
| 164 | try expect(ilogb128(0.8923) == -1); | |
| 165 | try expect(ilogb128(10.0) == 3); | |
| 166 | try expect(ilogb128(-123984) == 16); | |
| 167 | try expect(ilogb128(2398.23) == 11); | |
| 168 | } | |
| 169 | ||
| 170 | test "32 special" { | |
| 127 | 171 | try expect(ilogb32(math.inf(f32)) == maxInt(i32)); |
| 128 | 172 | try expect(ilogb32(-math.inf(f32)) == maxInt(i32)); |
| 129 | 173 | try expect(ilogb32(0.0) == minInt(i32)); |
| 130 | 174 | try expect(ilogb32(math.nan(f32)) == maxInt(i32)); |
| 131 | 175 | } |
| 132 | 176 | |
| 133 | test "math.ilogb64.special" { | |
| 177 | test "64 special" { | |
| 134 | 178 | try expect(ilogb64(math.inf(f64)) == maxInt(i32)); |
| 135 | 179 | try expect(ilogb64(-math.inf(f64)) == maxInt(i32)); |
| 136 | 180 | try expect(ilogb64(0.0) == minInt(i32)); |
| 137 | 181 | try expect(ilogb64(math.nan(f64)) == maxInt(i32)); |
| 138 | 182 | } |
| 183 | ||
| 184 | test "128 special" { | |
| 185 | try expect(ilogb128(math.inf(f128)) == maxInt(i32)); | |
| 186 | try expect(ilogb128(-math.inf(f128)) == maxInt(i32)); | |
| 187 | try expect(ilogb128(0.0) == minInt(i32)); | |
| 188 | try expect(ilogb128(math.nan(f128)) == maxInt(i32)); | |
| 189 | } |
lib/std/special/c_stage1.zig+4| ... | ... | @@ -656,6 +656,10 @@ export fn ceil(x: f64) f64 { |
| 656 | 656 | return math.ceil(x); |
| 657 | 657 | } |
| 658 | 658 | |
| 659 | export fn fmal(a: f128, b: f128, c: f128) f128 { | |
| 660 | return math.fma(f128, a, b, c); | |
| 661 | } | |
| 662 | ||
| 659 | 663 | export fn fma(a: f64, b: f64, c: f64) f64 { |
| 660 | 664 | return math.fma(f64, a, b, c); |
| 661 | 665 | } |
test/behavior/muladd.zig+6-7| ... | ... | @@ -24,11 +24,10 @@ fn testMulAdd() !void { |
| 24 | 24 | var c: f64 = 6.25; |
| 25 | 25 | try expect(@mulAdd(f64, a, b, c) == 20); |
| 26 | 26 | } |
| 27 | // Awaits implementation in libm.zig | |
| 28 | //{ | |
| 29 | // var a: f16 = 5.5; | |
| 30 | // var b: f128 = 2.5; | |
| 31 | // var c: f128 = 6.25; | |
| 32 | //try expect(@mulAdd(f128, a, b, c) == 20); | |
| 33 | //} | |
| 27 | { | |
| 28 | var a: f16 = 5.5; | |
| 29 | var b: f128 = 2.5; | |
| 30 | var c: f128 = 6.25; | |
| 31 | try expect(@mulAdd(f128, a, b, c) == 20); | |
| 32 | } | |
| 34 | 33 | } |