| author | |
| committer | |
| log | 77a4e7b37468ee7fb5dd3193972050808732f66c |
| tree | bf3a148e083b4414c32f7795f751b90f5ceec91e |
| parent | 5736a9c6a9b357ab346dd8fcbe64f5d729d6d244 |
| parent | 170ec504ec3201a89cb8121ea59e5d845f5cd1d1 |
| signature |
Add float parsing support to std15 files changed, 853 insertions(+), 10 deletions(-)
CMakeLists.txt+2| ... | ... | @@ -482,6 +482,7 @@ set(ZIG_STD_FILES |
| 482 | 482 | "fmt/errol/index.zig" |
| 483 | 483 | "fmt/errol/lookup.zig" |
| 484 | 484 | "fmt/index.zig" |
| 485 | "fmt/parse_float.zig" | |
| 485 | 486 | "hash/adler.zig" |
| 486 | 487 | "hash/crc.zig" |
| 487 | 488 | "hash/fnv.zig" |
| ... | ... | @@ -608,6 +609,7 @@ set(ZIG_STD_FILES |
| 608 | 609 | "special/bootstrap_lib.zig" |
| 609 | 610 | "special/build_runner.zig" |
| 610 | 611 | "special/builtin.zig" |
| 612 | "special/compiler_rt/addXf3.zig" | |
| 611 | 613 | "special/compiler_rt/aulldiv.zig" |
| 612 | 614 | "special/compiler_rt/aullrem.zig" |
| 613 | 615 | "special/compiler_rt/comparetf2.zig" |
std/fmt/index.zig+7-1| ... | ... | @@ -831,7 +831,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned |
| 831 | 831 | return x; |
| 832 | 832 | } |
| 833 | 833 | |
| 834 | test "parseUnsigned" { | |
| 834 | test "fmt.parseUnsigned" { | |
| 835 | 835 | testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); |
| 836 | 836 | testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); |
| 837 | 837 | testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10)); |
| ... | ... | @@ -858,6 +858,12 @@ test "parseUnsigned" { |
| 858 | 858 | testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16)); |
| 859 | 859 | } |
| 860 | 860 | |
| 861 | pub const parseFloat = @import("parse_float.zig").parseFloat; | |
| 862 | ||
| 863 | test "fmt.parseFloat" { | |
| 864 | _ = @import("parse_float.zig"); | |
| 865 | } | |
| 866 | ||
| 861 | 867 | pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { |
| 862 | 868 | const value = switch (c) { |
| 863 | 869 | '0'...'9' => c - '0', |
std/fmt/parse_float.zig created+420| ... | ... | @@ -0,0 +1,420 @@ |
| 1 | // Adapted from https://github.com/grzegorz-kraszewski/stringtofloat. | |
| 2 | ||
| 3 | // MIT License | |
| 4 | // | |
| 5 | // Copyright (c) 2016 Grzegorz Kraszewski | |
| 6 | // | |
| 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 8 | // of this software and associated documentation files (the "Software"), to deal | |
| 9 | // in the Software without restriction, including without limitation the rights | |
| 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 11 | // copies of the Software, and to permit persons to whom the Software is | |
| 12 | // furnished to do so, subject to the following conditions: | |
| 13 | // | |
| 14 | // The above copyright notice and this permission notice shall be included in all | |
| 15 | // copies or substantial portions of the Software. | |
| 16 | // | |
| 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| 23 | // SOFTWARE. | |
| 24 | // | |
| 25 | ||
| 26 | // Be aware that this implementation has the following limitations: | |
| 27 | // | |
| 28 | // - Is not round-trip accurate for all values | |
| 29 | // - Only supports round-to-zero | |
| 30 | // - Does not handle denormals | |
| 31 | ||
| 32 | const std = @import("../index.zig"); | |
| 33 | ||
| 34 | const max_digits = 25; | |
| 35 | ||
| 36 | const f64_plus_zero: u64 = 0x0000000000000000; | |
| 37 | const f64_minus_zero: u64 = 0x8000000000000000; | |
| 38 | const f64_plus_infinity: u64 = 0x7FF0000000000000; | |
| 39 | const f64_minus_infinity: u64 = 0xFFF0000000000000; | |
| 40 | ||
| 41 | const Z96 = struct { | |
| 42 | d0: u32, | |
| 43 | d1: u32, | |
| 44 | d2: u32, | |
| 45 | ||
| 46 | // d = s >> 1 | |
| 47 | inline fn shiftRight1(d: *Z96, s: Z96) void { | |
| 48 | d.d0 = (s.d0 >> 1) | ((s.d1 & 1) << 31); | |
| 49 | d.d1 = (s.d1 >> 1) | ((s.d2 & 1) << 31); | |
| 50 | d.d2 = s.d2 >> 1; | |
| 51 | } | |
| 52 | ||
| 53 | // d = s << 1 | |
| 54 | inline fn shiftLeft1(d: *Z96, s: Z96) void { | |
| 55 | d.d2 = (s.d2 << 1) | ((s.d1 & (1 << 31)) >> 31); | |
| 56 | d.d1 = (s.d1 << 1) | ((s.d0 & (1 << 31)) >> 31); | |
| 57 | d.d0 = s.d0 << 1; | |
| 58 | } | |
| 59 | ||
| 60 | // d += s | |
| 61 | inline fn add(d: *Z96, s: Z96) void { | |
| 62 | var w = u64(d.d0) + u64(s.d0); | |
| 63 | d.d0 = @truncate(u32, w); | |
| 64 | ||
| 65 | w >>= 32; | |
| 66 | w += u64(d.d1) + u64(s.d1); | |
| 67 | d.d1 = @truncate(u32, w); | |
| 68 | ||
| 69 | w >>= 32; | |
| 70 | w += u64(d.d2) + u64(s.d2); | |
| 71 | d.d2 = @truncate(u32, w); | |
| 72 | } | |
| 73 | ||
| 74 | // d -= s | |
| 75 | inline fn sub(d: *Z96, s: Z96) void { | |
| 76 | var w = u64(d.d0) -% u64(s.d0); | |
| 77 | d.d0 = @truncate(u32, w); | |
| 78 | ||
| 79 | w >>= 32; | |
| 80 | w += u64(d.d1) -% u64(s.d1); | |
| 81 | d.d1 = @truncate(u32, w); | |
| 82 | ||
| 83 | w >>= 32; | |
| 84 | w += u64(d.d2) -% u64(s.d2); | |
| 85 | d.d2 = @truncate(u32, w); | |
| 86 | } | |
| 87 | }; | |
| 88 | ||
| 89 | const FloatRepr = struct { | |
| 90 | negative: bool, | |
| 91 | exponent: i32, | |
| 92 | mantissa: u64, | |
| 93 | }; | |
| 94 | ||
| 95 | fn convertRepr(comptime T: type, n: FloatRepr) T { | |
| 96 | const mask28: u32 = 0xf << 28; | |
| 97 | ||
| 98 | var s: Z96 = undefined; | |
| 99 | var q: Z96 = undefined; | |
| 100 | var r: Z96 = undefined; | |
| 101 | ||
| 102 | s.d0 = @truncate(u32, n.mantissa); | |
| 103 | s.d1 = @truncate(u32, n.mantissa >> 32); | |
| 104 | s.d2 = 0; | |
| 105 | ||
| 106 | var binary_exponent: u64 = 92; | |
| 107 | var exp = n.exponent; | |
| 108 | ||
| 109 | while (exp > 0) : (exp -= 1) { | |
| 110 | q.shiftLeft1(s); // q = p << 1 | |
| 111 | r.shiftLeft1(q); // r = p << 2 | |
| 112 | s.shiftLeft1(r); // p = p << 3 | |
| 113 | q.add(s); // p = (p << 3) + (p << 1) | |
| 114 | ||
| 115 | exp -= 1; | |
| 116 | ||
| 117 | while (s.d2 & mask28 != 0) { | |
| 118 | q.shiftRight1(s); | |
| 119 | binary_exponent += 1; | |
| 120 | s = q; | |
| 121 | } | |
| 122 | } | |
| 123 | ||
| 124 | while (exp < 0) { | |
| 125 | while (s.d2 & (1 << 31) == 0) { | |
| 126 | q.shiftLeft1(s); | |
| 127 | binary_exponent -= 1; | |
| 128 | s = q; | |
| 129 | } | |
| 130 | ||
| 131 | q.d2 = s.d2 / 10; | |
| 132 | r.d1 = s.d2 % 10; | |
| 133 | r.d2 = (s.d1 >> 8) | (r.d1 << 24); | |
| 134 | q.d1 = r.d2 / 10; | |
| 135 | r.d1 = r.d2 % 10; | |
| 136 | r.d2 = ((s.d1 & 0xff) << 16) | (s.d0 >> 16) | (r.d1 << 24); | |
| 137 | r.d0 = r.d2 / 10; | |
| 138 | r.d1 = r.d2 % 10; | |
| 139 | q.d1 = (q.d1 << 8) | ((r.d0 & 0x00ff0000) >> 16); | |
| 140 | q.d0 = r.d0 << 16; | |
| 141 | r.d2 = (s.d0 *% 0xffff) | (r.d1 << 16); | |
| 142 | q.d0 |= r.d2 / 10; | |
| 143 | s = q; | |
| 144 | ||
| 145 | exp += 1; | |
| 146 | } | |
| 147 | ||
| 148 | if (s.d0 != 0 or s.d1 != 0 or s.d2 != 0) { | |
| 149 | while (s.d2 & mask28 == 0) { | |
| 150 | q.shiftLeft1(s); | |
| 151 | binary_exponent -= 1; | |
| 152 | s = q; | |
| 153 | } | |
| 154 | } | |
| 155 | ||
| 156 | binary_exponent += 1023; | |
| 157 | ||
| 158 | const repr: u64 = blk: { | |
| 159 | if (binary_exponent > 2046) { | |
| 160 | break :blk if (n.negative) f64_minus_infinity else f64_plus_infinity; | |
| 161 | } else if (binary_exponent < 1) { | |
| 162 | break :blk if (n.negative) f64_minus_zero else f64_plus_zero; | |
| 163 | } else if (s.d2 != 0) { | |
| 164 | const binexs2 = u64(binary_exponent) << 52; | |
| 165 | const rr = (u64(s.d2 & ~mask28) << 24) | ((u64(s.d1) + 128) >> 8) | binexs2; | |
| 166 | break :blk if (n.negative) rr | (1 << 63) else rr; | |
| 167 | } else { | |
| 168 | break :blk 0; | |
| 169 | } | |
| 170 | }; | |
| 171 | ||
| 172 | const f = @bitCast(f64, repr); | |
| 173 | return @floatCast(T, f); | |
| 174 | } | |
| 175 | ||
| 176 | const State = enum { | |
| 177 | MaybeSign, | |
| 178 | LeadingMantissaZeros, | |
| 179 | LeadingFractionalZeros, | |
| 180 | MantissaIntegral, | |
| 181 | MantissaFractional, | |
| 182 | ExponentSign, | |
| 183 | LeadingExponentZeros, | |
| 184 | Exponent, | |
| 185 | }; | |
| 186 | ||
| 187 | const ParseResult = enum { | |
| 188 | Ok, | |
| 189 | PlusZero, | |
| 190 | MinusZero, | |
| 191 | PlusInf, | |
| 192 | MinusInf, | |
| 193 | }; | |
| 194 | ||
| 195 | inline fn isDigit(c: u8) bool { | |
| 196 | return c >= '0' and c <= '9'; | |
| 197 | } | |
| 198 | ||
| 199 | inline fn isSpace(c: u8) bool { | |
| 200 | return (c >= 0x09 and c <= 0x13) or c == 0x20; | |
| 201 | } | |
| 202 | ||
| 203 | fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult { | |
| 204 | var digit_index: usize = 0; | |
| 205 | var negative = false; | |
| 206 | var negative_exp = false; | |
| 207 | var exponent: i32 = 0; | |
| 208 | ||
| 209 | var state = State.MaybeSign; | |
| 210 | ||
| 211 | var i: usize = 0; | |
| 212 | loop: while (i < s.len) { | |
| 213 | const c = s[i]; | |
| 214 | ||
| 215 | switch (state) { | |
| 216 | State.MaybeSign => { | |
| 217 | state = State.LeadingMantissaZeros; | |
| 218 | ||
| 219 | if (c == '+') { | |
| 220 | i += 1; | |
| 221 | } else if (c == '-') { | |
| 222 | n.negative = true; | |
| 223 | i += 1; | |
| 224 | } else if (isDigit(c) or c == '.') { | |
| 225 | // continue | |
| 226 | } else { | |
| 227 | return error.InvalidCharacter; | |
| 228 | } | |
| 229 | }, | |
| 230 | ||
| 231 | State.LeadingMantissaZeros => { | |
| 232 | if (c == '0') { | |
| 233 | i += 1; | |
| 234 | } else if (c == '.') { | |
| 235 | i += 1; | |
| 236 | state = State.LeadingFractionalZeros; | |
| 237 | } else { | |
| 238 | state = State.MantissaIntegral; | |
| 239 | } | |
| 240 | }, | |
| 241 | ||
| 242 | State.LeadingFractionalZeros => { | |
| 243 | if (c == '0') { | |
| 244 | i += 1; | |
| 245 | if (n.exponent > std.math.minInt(i32)) { | |
| 246 | n.exponent -= 1; | |
| 247 | } | |
| 248 | } else { | |
| 249 | state = State.MantissaFractional; | |
| 250 | } | |
| 251 | }, | |
| 252 | ||
| 253 | State.MantissaIntegral => { | |
| 254 | if (isDigit(c)) { | |
| 255 | if (digit_index < max_digits) { | |
| 256 | n.mantissa *%= 10; | |
| 257 | n.mantissa += s[i] - '0'; | |
| 258 | digit_index += 1; | |
| 259 | } else if (n.exponent < std.math.maxInt(i32)) { | |
| 260 | n.exponent += 1; | |
| 261 | } | |
| 262 | ||
| 263 | i += 1; | |
| 264 | } else if (c == '.') { | |
| 265 | i += 1; | |
| 266 | state = State.MantissaFractional; | |
| 267 | } else { | |
| 268 | state = State.MantissaFractional; | |
| 269 | } | |
| 270 | }, | |
| 271 | ||
| 272 | State.MantissaFractional => { | |
| 273 | if (isDigit(c)) { | |
| 274 | if (digit_index < max_digits) { | |
| 275 | n.mantissa *%= 10; | |
| 276 | n.mantissa += c - '0'; | |
| 277 | n.exponent -%= 1; | |
| 278 | digit_index += 1; | |
| 279 | } | |
| 280 | ||
| 281 | i += 1; | |
| 282 | } else if (c == 'e' or c == 'E') { | |
| 283 | i += 1; | |
| 284 | state = State.ExponentSign; | |
| 285 | } else { | |
| 286 | state = State.ExponentSign; | |
| 287 | } | |
| 288 | }, | |
| 289 | ||
| 290 | State.ExponentSign => { | |
| 291 | if (c == '+') { | |
| 292 | i += 1; | |
| 293 | } else if (c == '-') { | |
| 294 | negative_exp = true; | |
| 295 | i += 1; | |
| 296 | } | |
| 297 | ||
| 298 | state = State.LeadingExponentZeros; | |
| 299 | }, | |
| 300 | ||
| 301 | State.LeadingExponentZeros => { | |
| 302 | if (c == '0') { | |
| 303 | i += 1; | |
| 304 | } else { | |
| 305 | state = State.Exponent; | |
| 306 | } | |
| 307 | }, | |
| 308 | ||
| 309 | State.Exponent => { | |
| 310 | if (isDigit(c)) { | |
| 311 | if (exponent < std.math.maxInt(i32)) { | |
| 312 | exponent *= 10; | |
| 313 | exponent += @intCast(i32, c - '0'); | |
| 314 | } | |
| 315 | ||
| 316 | i += 1; | |
| 317 | } else { | |
| 318 | return error.InvalidCharacter; | |
| 319 | } | |
| 320 | }, | |
| 321 | } | |
| 322 | } | |
| 323 | ||
| 324 | if (negative_exp) exponent = -exponent; | |
| 325 | n.exponent += exponent; | |
| 326 | ||
| 327 | if (n.mantissa == 0) { | |
| 328 | return if (n.negative) ParseResult.MinusZero else ParseResult.PlusZero; | |
| 329 | } else if (n.exponent > 309) { | |
| 330 | return if (n.negative) ParseResult.MinusInf else ParseResult.PlusInf; | |
| 331 | } else if (n.exponent < -328) { | |
| 332 | return if (n.negative) ParseResult.MinusZero else ParseResult.PlusZero; | |
| 333 | } | |
| 334 | ||
| 335 | return ParseResult.Ok; | |
| 336 | } | |
| 337 | ||
| 338 | inline fn isLower(c: u8) bool { | |
| 339 | return c -% 'a' < 26; | |
| 340 | } | |
| 341 | ||
| 342 | inline fn toUpper(c: u8) u8 { | |
| 343 | return if (isLower(c)) (c & 0x5f) else c; | |
| 344 | } | |
| 345 | ||
| 346 | fn caseInEql(a: []const u8, b: []const u8) bool { | |
| 347 | if (a.len != b.len) return false; | |
| 348 | ||
| 349 | for (a) |_, i| { | |
| 350 | if (toUpper(a[i]) != toUpper(b[i])) { | |
| 351 | return false; | |
| 352 | } | |
| 353 | } | |
| 354 | ||
| 355 | return true; | |
| 356 | } | |
| 357 | ||
| 358 | pub fn parseFloat(comptime T: type, s: []const u8) !T { | |
| 359 | if (s.len == 0) { | |
| 360 | return error.InvalidCharacter; | |
| 361 | } | |
| 362 | ||
| 363 | if (caseInEql(s, "nan")) { | |
| 364 | return std.math.nan(T); | |
| 365 | } else if (caseInEql(s, "inf") or caseInEql(s, "+inf")) { | |
| 366 | return std.math.inf(T); | |
| 367 | } else if (caseInEql(s, "-inf")) { | |
| 368 | return -std.math.inf(T); | |
| 369 | } | |
| 370 | ||
| 371 | var r = FloatRepr{ | |
| 372 | .negative = false, | |
| 373 | .exponent = 0, | |
| 374 | .mantissa = 0, | |
| 375 | }; | |
| 376 | ||
| 377 | return switch (try parseRepr(s, &r)) { | |
| 378 | ParseResult.Ok => convertRepr(T, r), | |
| 379 | ParseResult.PlusZero => 0.0, | |
| 380 | ParseResult.MinusZero => -T(0.0), | |
| 381 | ParseResult.PlusInf => std.math.inf(T), | |
| 382 | ParseResult.MinusInf => -std.math.inf(T), | |
| 383 | }; | |
| 384 | } | |
| 385 | ||
| 386 | test "fmt.parseFloat" { | |
| 387 | const testing = std.testing; | |
| 388 | const expect = testing.expect; | |
| 389 | const expectEqual = testing.expectEqual; | |
| 390 | const approxEq = std.math.approxEq; | |
| 391 | const epsilon = 1e-7; | |
| 392 | ||
| 393 | inline for ([]type{ f16, f32, f64, f128 }) |T| { | |
| 394 | const Z = @IntType(false, T.bit_count); | |
| 395 | ||
| 396 | testing.expectError(error.InvalidCharacter, parseFloat(T, "")); | |
| 397 | testing.expectError(error.InvalidCharacter, parseFloat(T, " 1")); | |
| 398 | testing.expectError(error.InvalidCharacter, parseFloat(T, "1abc")); | |
| 399 | ||
| 400 | expectEqual(try parseFloat(T, "0"), 0.0); | |
| 401 | expectEqual((try parseFloat(T, "0")), 0.0); | |
| 402 | expectEqual((try parseFloat(T, "+0")), 0.0); | |
| 403 | expectEqual((try parseFloat(T, "-0")), 0.0); | |
| 404 | ||
| 405 | expect(approxEq(T, try parseFloat(T, "3.141"), 3.141, epsilon)); | |
| 406 | expect(approxEq(T, try parseFloat(T, "-3.141"), -3.141, epsilon)); | |
| 407 | ||
| 408 | expectEqual((try parseFloat(T, "1e-700")), 0); | |
| 409 | expectEqual((try parseFloat(T, "1e+700")), std.math.inf(T)); | |
| 410 | ||
| 411 | expectEqual(@bitCast(Z, try parseFloat(T, "nAn")), @bitCast(Z, std.math.nan(T))); | |
| 412 | expectEqual((try parseFloat(T, "inF")), std.math.inf(T)); | |
| 413 | expectEqual((try parseFloat(T, "-INF")), -std.math.inf(T)); | |
| 414 | ||
| 415 | if (T != f16) { | |
| 416 | expect(approxEq(T, try parseFloat(T, "123142.1"), 123142.1, epsilon)); | |
| 417 | expect(approxEq(T, try parseFloat(T, "-123142.1124"), T(-123142.1124), epsilon)); | |
| 418 | } | |
| 419 | } | |
| 420 | } |
std/json.zig+6-2| ... | ... | @@ -1345,7 +1345,7 @@ pub const Parser = struct { |
| 1345 | 1345 | return if (token.number_is_integer) |
| 1346 | 1346 | Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } |
| 1347 | 1347 | else |
| 1348 | @panic("TODO: fmt.parseFloat not yet implemented"); | |
| 1348 | Value{ .Float = try std.fmt.parseFloat(f64, token.slice(input, i)) }; | |
| 1349 | 1349 | } |
| 1350 | 1350 | }; |
| 1351 | 1351 | |
| ... | ... | @@ -1366,7 +1366,8 @@ test "json.parser.dynamic" { |
| 1366 | 1366 | \\ }, |
| 1367 | 1367 | \\ "Animated" : false, |
| 1368 | 1368 | \\ "IDs": [116, 943, 234, 38793], |
| 1369 | \\ "ArrayOfObject": [{"n": "m"}] | |
| 1369 | \\ "ArrayOfObject": [{"n": "m"}], | |
| 1370 | \\ "double": 1.3412 | |
| 1370 | 1371 | \\ } |
| 1371 | 1372 | \\} |
| 1372 | 1373 | ; |
| ... | ... | @@ -1395,4 +1396,7 @@ test "json.parser.dynamic" { |
| 1395 | 1396 | |
| 1396 | 1397 | const obj0 = array_of_object.Array.at(0).Object.get("n").?.value; |
| 1397 | 1398 | testing.expect(mem.eql(u8, obj0.String, "m")); |
| 1399 | ||
| 1400 | const double = image.Object.get("double").?.value; | |
| 1401 | testing.expect(double.Float == 1.3412); | |
| 1398 | 1402 | } |
std/math/fabs.zig+19| ... | ... | @@ -14,6 +14,7 @@ pub fn fabs(x: var) @typeOf(x) { |
| 14 | 14 | f16 => fabs16(x), |
| 15 | 15 | f32 => fabs32(x), |
| 16 | 16 | f64 => fabs64(x), |
| 17 | f128 => fabs128(x), | |
| 17 | 18 | else => @compileError("fabs not implemented for " ++ @typeName(T)), |
| 18 | 19 | }; |
| 19 | 20 | } |
| ... | ... | @@ -36,10 +37,17 @@ fn fabs64(x: f64) f64 { |
| 36 | 37 | return @bitCast(f64, u); |
| 37 | 38 | } |
| 38 | 39 | |
| 40 | fn fabs128(x: f128) f128 { | |
| 41 | var u = @bitCast(u128, x); | |
| 42 | u &= maxInt(u128) >> 1; | |
| 43 | return @bitCast(f128, u); | |
| 44 | } | |
| 45 | ||
| 39 | 46 | test "math.fabs" { |
| 40 | 47 | expect(fabs(f16(1.0)) == fabs16(1.0)); |
| 41 | 48 | expect(fabs(f32(1.0)) == fabs32(1.0)); |
| 42 | 49 | expect(fabs(f64(1.0)) == fabs64(1.0)); |
| 50 | expect(fabs(f128(1.0)) == fabs128(1.0)); | |
| 43 | 51 | } |
| 44 | 52 | |
| 45 | 53 | test "math.fabs16" { |
| ... | ... | @@ -57,6 +65,11 @@ test "math.fabs64" { |
| 57 | 65 | expect(fabs64(-1.0) == 1.0); |
| 58 | 66 | } |
| 59 | 67 | |
| 68 | test "math.fabs128" { | |
| 69 | expect(fabs128(1.0) == 1.0); | |
| 70 | expect(fabs128(-1.0) == 1.0); | |
| 71 | } | |
| 72 | ||
| 60 | 73 | test "math.fabs16.special" { |
| 61 | 74 | expect(math.isPositiveInf(fabs(math.inf(f16)))); |
| 62 | 75 | expect(math.isPositiveInf(fabs(-math.inf(f16)))); |
| ... | ... | @@ -74,3 +87,9 @@ test "math.fabs64.special" { |
| 74 | 87 | expect(math.isPositiveInf(fabs(-math.inf(f64)))); |
| 75 | 88 | expect(math.isNan(fabs(math.nan(f64)))); |
| 76 | 89 | } |
| 90 | ||
| 91 | test "math.fabs128.special" { | |
| 92 | expect(math.isPositiveInf(fabs(math.inf(f128)))); | |
| 93 | expect(math.isPositiveInf(fabs(-math.inf(f128)))); | |
| 94 | expect(math.isNan(fabs(math.nan(f128)))); | |
| 95 | } |
std/math/index.zig+11-1| ... | ... | @@ -51,6 +51,12 @@ pub const nan_f64 = @bitCast(f64, nan_u64); |
| 51 | 51 | pub const inf_u64 = u64(0x7FF << 52); |
| 52 | 52 | pub const inf_f64 = @bitCast(f64, inf_u64); |
| 53 | 53 | |
| 54 | pub const nan_u128 = u128(0x7fff0000000000000000000000000001); | |
| 55 | pub const nan_f128 = @bitCast(f128, nan_u128); | |
| 56 | ||
| 57 | pub const inf_u128 = u128(0x7fff0000000000000000000000000000); | |
| 58 | pub const inf_f128 = @bitCast(f128, inf_u128); | |
| 59 | ||
| 54 | 60 | pub const nan = @import("nan.zig").nan; |
| 55 | 61 | pub const snan = @import("nan.zig").snan; |
| 56 | 62 | pub const inf = @import("inf.zig").inf; |
| ... | ... | @@ -379,7 +385,7 @@ pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) t |
| 379 | 385 | return u0; |
| 380 | 386 | } |
| 381 | 387 | const is_signed = from < 0; |
| 382 | const largest_positive_integer = max(if (from<0) (-from)-1 else from, to); // two's complement | |
| 388 | const largest_positive_integer = max(if (from < 0) (-from) - 1 else from, to); // two's complement | |
| 383 | 389 | const base = log2(largest_positive_integer); |
| 384 | 390 | const upper = (1 << base) - 1; |
| 385 | 391 | var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1; |
| ... | ... | @@ -752,6 +758,7 @@ test "minInt and maxInt" { |
| 752 | 758 | testing.expect(maxInt(u16) == 65535); |
| 753 | 759 | testing.expect(maxInt(u32) == 4294967295); |
| 754 | 760 | testing.expect(maxInt(u64) == 18446744073709551615); |
| 761 | testing.expect(maxInt(u128) == 340282366920938463463374607431768211455); | |
| 755 | 762 | |
| 756 | 763 | testing.expect(maxInt(i0) == 0); |
| 757 | 764 | testing.expect(maxInt(i1) == 0); |
| ... | ... | @@ -760,6 +767,7 @@ test "minInt and maxInt" { |
| 760 | 767 | testing.expect(maxInt(i32) == 2147483647); |
| 761 | 768 | testing.expect(maxInt(i63) == 4611686018427387903); |
| 762 | 769 | testing.expect(maxInt(i64) == 9223372036854775807); |
| 770 | testing.expect(maxInt(i128) == 170141183460469231731687303715884105727); | |
| 763 | 771 | |
| 764 | 772 | testing.expect(minInt(u0) == 0); |
| 765 | 773 | testing.expect(minInt(u1) == 0); |
| ... | ... | @@ -768,6 +776,7 @@ test "minInt and maxInt" { |
| 768 | 776 | testing.expect(minInt(u32) == 0); |
| 769 | 777 | testing.expect(minInt(u63) == 0); |
| 770 | 778 | testing.expect(minInt(u64) == 0); |
| 779 | testing.expect(minInt(u128) == 0); | |
| 771 | 780 | |
| 772 | 781 | testing.expect(minInt(i0) == 0); |
| 773 | 782 | testing.expect(minInt(i1) == -1); |
| ... | ... | @@ -776,6 +785,7 @@ test "minInt and maxInt" { |
| 776 | 785 | testing.expect(minInt(i32) == -2147483648); |
| 777 | 786 | testing.expect(minInt(i63) == -4611686018427387904); |
| 778 | 787 | testing.expect(minInt(i64) == -9223372036854775808); |
| 788 | testing.expect(minInt(i128) == -170141183460469231731687303715884105728); | |
| 779 | 789 | } |
| 780 | 790 | |
| 781 | 791 | test "max value type" { |
std/math/inf.zig+4-3| ... | ... | @@ -3,9 +3,10 @@ const math = std.math; |
| 3 | 3 | |
| 4 | 4 | pub fn inf(comptime T: type) T { |
| 5 | 5 | return switch (T) { |
| 6 | f16 => @bitCast(f16, math.inf_u16), | |
| 7 | f32 => @bitCast(f32, math.inf_u32), | |
| 8 | f64 => @bitCast(f64, math.inf_u64), | |
| 6 | f16 => math.inf_f16, | |
| 7 | f32 => math.inf_f32, | |
| 8 | f64 => math.inf_f64, | |
| 9 | f128 => math.inf_f128, | |
| 9 | 10 | else => @compileError("inf not implemented for " ++ @typeName(T)), |
| 10 | 11 | }; |
| 11 | 12 | } |
std/math/isinf.zig+22| ... | ... | @@ -18,6 +18,10 @@ pub fn isInf(x: var) bool { |
| 18 | 18 | const bits = @bitCast(u64, x); |
| 19 | 19 | return bits & (maxInt(u64) >> 1) == (0x7FF << 52); |
| 20 | 20 | }, |
| 21 | f128 => { | |
| 22 | const bits = @bitCast(u128, x); | |
| 23 | return bits & (maxInt(u128) >> 1) == (0x7FFF << 112); | |
| 24 | }, | |
| 21 | 25 | else => { |
| 22 | 26 | @compileError("isInf not implemented for " ++ @typeName(T)); |
| 23 | 27 | }, |
| ... | ... | @@ -36,6 +40,9 @@ pub fn isPositiveInf(x: var) bool { |
| 36 | 40 | f64 => { |
| 37 | 41 | return @bitCast(u64, x) == 0x7FF << 52; |
| 38 | 42 | }, |
| 43 | f128 => { | |
| 44 | return @bitCast(u128, x) == 0x7FFF << 112; | |
| 45 | }, | |
| 39 | 46 | else => { |
| 40 | 47 | @compileError("isPositiveInf not implemented for " ++ @typeName(T)); |
| 41 | 48 | }, |
| ... | ... | @@ -54,6 +61,9 @@ pub fn isNegativeInf(x: var) bool { |
| 54 | 61 | f64 => { |
| 55 | 62 | return @bitCast(u64, x) == 0xFFF << 52; |
| 56 | 63 | }, |
| 64 | f128 => { | |
| 65 | return @bitCast(u128, x) == 0xFFFF << 112; | |
| 66 | }, | |
| 57 | 67 | else => { |
| 58 | 68 | @compileError("isNegativeInf not implemented for " ++ @typeName(T)); |
| 59 | 69 | }, |
| ... | ... | @@ -67,12 +77,16 @@ test "math.isInf" { |
| 67 | 77 | expect(!isInf(f32(-0.0))); |
| 68 | 78 | expect(!isInf(f64(0.0))); |
| 69 | 79 | expect(!isInf(f64(-0.0))); |
| 80 | expect(!isInf(f128(0.0))); | |
| 81 | expect(!isInf(f128(-0.0))); | |
| 70 | 82 | expect(isInf(math.inf(f16))); |
| 71 | 83 | expect(isInf(-math.inf(f16))); |
| 72 | 84 | expect(isInf(math.inf(f32))); |
| 73 | 85 | expect(isInf(-math.inf(f32))); |
| 74 | 86 | expect(isInf(math.inf(f64))); |
| 75 | 87 | expect(isInf(-math.inf(f64))); |
| 88 | expect(isInf(math.inf(f128))); | |
| 89 | expect(isInf(-math.inf(f128))); | |
| 76 | 90 | } |
| 77 | 91 | |
| 78 | 92 | test "math.isPositiveInf" { |
| ... | ... | @@ -82,12 +96,16 @@ test "math.isPositiveInf" { |
| 82 | 96 | expect(!isPositiveInf(f32(-0.0))); |
| 83 | 97 | expect(!isPositiveInf(f64(0.0))); |
| 84 | 98 | expect(!isPositiveInf(f64(-0.0))); |
| 99 | expect(!isPositiveInf(f128(0.0))); | |
| 100 | expect(!isPositiveInf(f128(-0.0))); | |
| 85 | 101 | expect(isPositiveInf(math.inf(f16))); |
| 86 | 102 | expect(!isPositiveInf(-math.inf(f16))); |
| 87 | 103 | expect(isPositiveInf(math.inf(f32))); |
| 88 | 104 | expect(!isPositiveInf(-math.inf(f32))); |
| 89 | 105 | expect(isPositiveInf(math.inf(f64))); |
| 90 | 106 | expect(!isPositiveInf(-math.inf(f64))); |
| 107 | expect(isPositiveInf(math.inf(f128))); | |
| 108 | expect(!isPositiveInf(-math.inf(f128))); | |
| 91 | 109 | } |
| 92 | 110 | |
| 93 | 111 | test "math.isNegativeInf" { |
| ... | ... | @@ -97,10 +115,14 @@ test "math.isNegativeInf" { |
| 97 | 115 | expect(!isNegativeInf(f32(-0.0))); |
| 98 | 116 | expect(!isNegativeInf(f64(0.0))); |
| 99 | 117 | expect(!isNegativeInf(f64(-0.0))); |
| 118 | expect(!isNegativeInf(f128(0.0))); | |
| 119 | expect(!isNegativeInf(f128(-0.0))); | |
| 100 | 120 | expect(!isNegativeInf(math.inf(f16))); |
| 101 | 121 | expect(isNegativeInf(-math.inf(f16))); |
| 102 | 122 | expect(!isNegativeInf(math.inf(f32))); |
| 103 | 123 | expect(isNegativeInf(-math.inf(f32))); |
| 104 | 124 | expect(!isNegativeInf(math.inf(f64))); |
| 105 | 125 | expect(isNegativeInf(-math.inf(f64))); |
| 126 | expect(!isNegativeInf(math.inf(f128))); | |
| 127 | expect(isNegativeInf(-math.inf(f128))); | |
| 106 | 128 | } |
std/math/isnan.zig+6| ... | ... | @@ -18,6 +18,10 @@ pub fn isNan(x: var) bool { |
| 18 | 18 | const bits = @bitCast(u64, x); |
| 19 | 19 | return (bits & (maxInt(u64) >> 1)) > (u64(0x7FF) << 52); |
| 20 | 20 | }, |
| 21 | f128 => { | |
| 22 | const bits = @bitCast(u128, x); | |
| 23 | return (bits & (maxInt(u128) >> 1)) > (u128(0x7FFF) << 112); | |
| 24 | }, | |
| 21 | 25 | else => { |
| 22 | 26 | @compileError("isNan not implemented for " ++ @typeName(T)); |
| 23 | 27 | }, |
| ... | ... | @@ -34,7 +38,9 @@ test "math.isNan" { |
| 34 | 38 | expect(isNan(math.nan(f16))); |
| 35 | 39 | expect(isNan(math.nan(f32))); |
| 36 | 40 | expect(isNan(math.nan(f64))); |
| 41 | expect(isNan(math.nan(f128))); | |
| 37 | 42 | expect(!isNan(f16(1.0))); |
| 38 | 43 | expect(!isNan(f32(1.0))); |
| 39 | 44 | expect(!isNan(f64(1.0))); |
| 45 | expect(!isNan(f128(1.0))); | |
| 40 | 46 | } |
std/math/nan.zig+4-3| ... | ... | @@ -2,9 +2,10 @@ const math = @import("index.zig"); |
| 2 | 2 | |
| 3 | 3 | pub fn nan(comptime T: type) T { |
| 4 | 4 | return switch (T) { |
| 5 | f16 => @bitCast(f16, math.nan_u16), | |
| 6 | f32 => @bitCast(f32, math.nan_u32), | |
| 7 | f64 => @bitCast(f64, math.nan_u64), | |
| 5 | f16 => math.nan_f16, | |
| 6 | f32 => math.nan_f32, | |
| 7 | f64 => math.nan_f64, | |
| 8 | f128 => math.nan_f128, | |
| 8 | 9 | else => @compileError("nan not implemented for " ++ @typeName(T)), |
| 9 | 10 | }; |
| 10 | 11 | } |
std/special/compiler_rt/addXf3.zig created+191| ... | ... | @@ -0,0 +1,191 @@ |
| 1 | // Ported from: | |
| 2 | // | |
| 3 | // https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/lib/builtins/fp_add_impl.inc | |
| 4 | ||
| 5 | const std = @import("std"); | |
| 6 | const builtin = @import("builtin"); | |
| 7 | const compiler_rt = @import("index.zig"); | |
| 8 | ||
| 9 | pub extern fn __addtf3(a: f128, b: f128) f128 { | |
| 10 | return addXf3(f128, a, b); | |
| 11 | } | |
| 12 | ||
| 13 | pub extern fn __subtf3(a: f128, b: f128) f128 { | |
| 14 | const neg_b = @bitCast(f128, @bitCast(u128, b) ^ (u128(1) << 127)); | |
| 15 | return addXf3(f128, a, neg_b); | |
| 16 | } | |
| 17 | ||
| 18 | inline fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 { | |
| 19 | const Z = @IntType(false, T.bit_count); | |
| 20 | const significandBits = std.math.floatMantissaBits(T); | |
| 21 | const implicitBit = Z(1) << significandBits; | |
| 22 | ||
| 23 | const shift = @clz(significand.*) - @clz(implicitBit); | |
| 24 | significand.* <<= @intCast(u7, shift); | |
| 25 | return 1 - shift; | |
| 26 | } | |
| 27 | ||
| 28 | inline fn addXf3(comptime T: type, a: T, b: T) T { | |
| 29 | const Z = @IntType(false, T.bit_count); | |
| 30 | ||
| 31 | const typeWidth = T.bit_count; | |
| 32 | const significandBits = std.math.floatMantissaBits(T); | |
| 33 | const exponentBits = std.math.floatExponentBits(T); | |
| 34 | ||
| 35 | const signBit = (Z(1) << (significandBits + exponentBits)); | |
| 36 | const maxExponent = ((1 << exponentBits) - 1); | |
| 37 | const exponentBias = (maxExponent >> 1); | |
| 38 | ||
| 39 | const implicitBit = (Z(1) << significandBits); | |
| 40 | const quietBit = implicitBit >> 1; | |
| 41 | const significandMask = implicitBit - 1; | |
| 42 | ||
| 43 | const absMask = signBit - 1; | |
| 44 | const exponentMask = absMask ^ significandMask; | |
| 45 | const qnanRep = exponentMask | quietBit; | |
| 46 | ||
| 47 | var aRep = @bitCast(Z, a); | |
| 48 | var bRep = @bitCast(Z, b); | |
| 49 | const aAbs = aRep & absMask; | |
| 50 | const bAbs = bRep & absMask; | |
| 51 | ||
| 52 | const negative = (aRep & signBit) != 0; | |
| 53 | const exponent = @intCast(i32, aAbs >> significandBits) - exponentBias; | |
| 54 | const significand = (aAbs & significandMask) | implicitBit; | |
| 55 | ||
| 56 | const infRep = @bitCast(Z, std.math.inf(T)); | |
| 57 | ||
| 58 | // Detect if a or b is zero, infinity, or NaN. | |
| 59 | if (aAbs - Z(1) >= infRep - Z(1) or | |
| 60 | bAbs - Z(1) >= infRep - Z(1)) | |
| 61 | { | |
| 62 | // NaN + anything = qNaN | |
| 63 | if (aAbs > infRep) return @bitCast(T, @bitCast(Z, a) | quietBit); | |
| 64 | // anything + NaN = qNaN | |
| 65 | if (bAbs > infRep) return @bitCast(T, @bitCast(Z, b) | quietBit); | |
| 66 | ||
| 67 | if (aAbs == infRep) { | |
| 68 | // +/-infinity + -/+infinity = qNaN | |
| 69 | if ((@bitCast(Z, a) ^ @bitCast(Z, b)) == signBit) { | |
| 70 | return @bitCast(T, qnanRep); | |
| 71 | } | |
| 72 | // +/-infinity + anything remaining = +/- infinity | |
| 73 | else { | |
| 74 | return a; | |
| 75 | } | |
| 76 | } | |
| 77 | ||
| 78 | // anything remaining + +/-infinity = +/-infinity | |
| 79 | if (bAbs == infRep) return b; | |
| 80 | ||
| 81 | // zero + anything = anything | |
| 82 | if (aAbs == 0) { | |
| 83 | // but we need to get the sign right for zero + zero | |
| 84 | if (bAbs == 0) { | |
| 85 | return @bitCast(T, @bitCast(Z, a) & @bitCast(Z, b)); | |
| 86 | } else { | |
| 87 | return b; | |
| 88 | } | |
| 89 | } | |
| 90 | ||
| 91 | // anything + zero = anything | |
| 92 | if (bAbs == 0) return a; | |
| 93 | } | |
| 94 | ||
| 95 | // Swap a and b if necessary so that a has the larger absolute value. | |
| 96 | if (bAbs > aAbs) { | |
| 97 | const temp = aRep; | |
| 98 | aRep = bRep; | |
| 99 | bRep = temp; | |
| 100 | } | |
| 101 | ||
| 102 | // Extract the exponent and significand from the (possibly swapped) a and b. | |
| 103 | var aExponent = @intCast(i32, (aRep >> significandBits) & maxExponent); | |
| 104 | var bExponent = @intCast(i32, (bRep >> significandBits) & maxExponent); | |
| 105 | var aSignificand = aRep & significandMask; | |
| 106 | var bSignificand = bRep & significandMask; | |
| 107 | ||
| 108 | // Normalize any denormals, and adjust the exponent accordingly. | |
| 109 | if (aExponent == 0) aExponent = normalize(T, &aSignificand); | |
| 110 | if (bExponent == 0) bExponent = normalize(T, &bSignificand); | |
| 111 | ||
| 112 | // The sign of the result is the sign of the larger operand, a. If they | |
| 113 | // have opposite signs, we are performing a subtraction; otherwise addition. | |
| 114 | const resultSign = aRep & signBit; | |
| 115 | const subtraction = (aRep ^ bRep) & signBit != 0; | |
| 116 | ||
| 117 | // Shift the significands to give us round, guard and sticky, and or in the | |
| 118 | // implicit significand bit. (If we fell through from the denormal path it | |
| 119 | // was already set by normalize( ), but setting it twice won't hurt | |
| 120 | // anything.) | |
| 121 | aSignificand = (aSignificand | implicitBit) << 3; | |
| 122 | bSignificand = (bSignificand | implicitBit) << 3; | |
| 123 | ||
| 124 | // Shift the significand of b by the difference in exponents, with a sticky | |
| 125 | // bottom bit to get rounding correct. | |
| 126 | const @"align" = @intCast(Z, aExponent - bExponent); | |
| 127 | if (@"align" != 0) { | |
| 128 | if (@"align" < typeWidth) { | |
| 129 | const sticky = if (bSignificand << @intCast(u7, typeWidth - @"align") != 0) Z(1) else 0; | |
| 130 | bSignificand = (bSignificand >> @truncate(u7, @"align")) | sticky; | |
| 131 | } else { | |
| 132 | bSignificand = 1; // sticky; b is known to be non-zero. | |
| 133 | } | |
| 134 | } | |
| 135 | if (subtraction) { | |
| 136 | aSignificand -= bSignificand; | |
| 137 | // If a == -b, return +zero. | |
| 138 | if (aSignificand == 0) return @bitCast(T, Z(0)); | |
| 139 | ||
| 140 | // If partial cancellation occured, we need to left-shift the result | |
| 141 | // and adjust the exponent: | |
| 142 | if (aSignificand < implicitBit << 3) { | |
| 143 | const shift = @intCast(i32, @clz(aSignificand)) - @intCast(i32, @clz(implicitBit << 3)); | |
| 144 | aSignificand <<= @intCast(u7, shift); | |
| 145 | aExponent -= shift; | |
| 146 | } | |
| 147 | } else { // addition | |
| 148 | aSignificand += bSignificand; | |
| 149 | ||
| 150 | // If the addition carried up, we need to right-shift the result and | |
| 151 | // adjust the exponent: | |
| 152 | if (aSignificand & (implicitBit << 4) != 0) { | |
| 153 | const sticky = aSignificand & 1; | |
| 154 | aSignificand = aSignificand >> 1 | sticky; | |
| 155 | aExponent += 1; | |
| 156 | } | |
| 157 | } | |
| 158 | ||
| 159 | // If we have overflowed the type, return +/- infinity: | |
| 160 | if (aExponent >= maxExponent) return @bitCast(T, infRep | resultSign); | |
| 161 | ||
| 162 | if (aExponent <= 0) { | |
| 163 | // Result is denormal before rounding; the exponent is zero and we | |
| 164 | // need to shift the significand. | |
| 165 | const shift = @intCast(Z, 1 - aExponent); | |
| 166 | const sticky = if (aSignificand << @intCast(u7, typeWidth - shift) != 0) Z(1) else 0; | |
| 167 | aSignificand = aSignificand >> @intCast(u7, shift | sticky); | |
| 168 | aExponent = 0; | |
| 169 | } | |
| 170 | ||
| 171 | // Low three bits are round, guard, and sticky. | |
| 172 | const roundGuardSticky = aSignificand & 0x7; | |
| 173 | ||
| 174 | // Shift the significand into place, and mask off the implicit bit. | |
| 175 | var result = (aSignificand >> 3) & significandMask; | |
| 176 | ||
| 177 | // Insert the exponent and sign. | |
| 178 | result |= @intCast(Z, aExponent) << significandBits; | |
| 179 | result |= resultSign; | |
| 180 | ||
| 181 | // Final rounding. The result may overflow to infinity, but that is the | |
| 182 | // correct result in that case. | |
| 183 | if (roundGuardSticky > 0x4) result += 1; | |
| 184 | if (roundGuardSticky == 0x4) result += result & 1; | |
| 185 | ||
| 186 | return @bitCast(T, result); | |
| 187 | } | |
| 188 | ||
| 189 | test "import addXf3" { | |
| 190 | _ = @import("addXf3_test.zig"); | |
| 191 | } |
std/special/compiler_rt/addXf3_test.zig created+85| ... | ... | @@ -0,0 +1,85 @@ |
| 1 | // Ported from: | |
| 2 | // | |
| 3 | // https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/test/builtins/Unit/addtf3_test.c | |
| 4 | // https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/test/builtins/Unit/subtf3_test.c | |
| 5 | ||
| 6 | const qnan128 = @bitCast(f128, u128(0x7fff800000000000) << 64); | |
| 7 | const inf128 = @bitCast(f128, u128(0x7fff000000000000) << 64); | |
| 8 | ||
| 9 | const __addtf3 = @import("addXf3.zig").__addtf3; | |
| 10 | ||
| 11 | fn test__addtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) void { | |
| 12 | const x = __addtf3(a, b); | |
| 13 | ||
| 14 | const rep = @bitCast(u128, x); | |
| 15 | const hi = @intCast(u64, rep >> 64); | |
| 16 | const lo = @truncate(u64, rep); | |
| 17 | ||
| 18 | if (hi == expected_hi and lo == expected_lo) { | |
| 19 | return; | |
| 20 | } | |
| 21 | // test other possible NaN representation (signal NaN) | |
| 22 | else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { | |
| 23 | if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and | |
| 24 | ((hi & 0xffffffffffff) > 0 or lo > 0)) | |
| 25 | { | |
| 26 | return; | |
| 27 | } | |
| 28 | } | |
| 29 | ||
| 30 | @panic("__addtf3 test failure"); | |
| 31 | } | |
| 32 | ||
| 33 | test "addtf3" { | |
| 34 | test__addtf3(qnan128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); | |
| 35 | ||
| 36 | // NaN + any = NaN | |
| 37 | test__addtf3(@bitCast(f128, (u128(0x7fff000000000000) << 64) | u128(0x800030000000)), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); | |
| 38 | ||
| 39 | // inf + inf = inf | |
| 40 | test__addtf3(inf128, inf128, 0x7fff000000000000, 0x0); | |
| 41 | ||
| 42 | // inf + any = inf | |
| 43 | test__addtf3(inf128, 0x1.2335653452436234723489432abcdefp+5, 0x7fff000000000000, 0x0); | |
| 44 | ||
| 45 | // any + any | |
| 46 | test__addtf3(0x1.23456734245345543849abcdefp+5, 0x1.edcba52449872455634654321fp-1, 0x40042afc95c8b579, 0x61e58dd6c51eb77c); | |
| 47 | } | |
| 48 | ||
| 49 | const __subtf3 = @import("addXf3.zig").__subtf3; | |
| 50 | ||
| 51 | fn test__subtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) void { | |
| 52 | const x = __subtf3(a, b); | |
| 53 | ||
| 54 | const rep = @bitCast(u128, x); | |
| 55 | const hi = @intCast(u64, rep >> 64); | |
| 56 | const lo = @truncate(u64, rep); | |
| 57 | ||
| 58 | if (hi == expected_hi and lo == expected_lo) { | |
| 59 | return; | |
| 60 | } | |
| 61 | // test other possible NaN representation (signal NaN) | |
| 62 | else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { | |
| 63 | if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and | |
| 64 | ((hi & 0xffffffffffff) > 0 or lo > 0)) | |
| 65 | { | |
| 66 | return; | |
| 67 | } | |
| 68 | } | |
| 69 | ||
| 70 | @panic("__subtf3 test failure"); | |
| 71 | } | |
| 72 | ||
| 73 | test "subtf3" { | |
| 74 | // qNaN - any = qNaN | |
| 75 | test__subtf3(qnan128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); | |
| 76 | ||
| 77 | // NaN + any = NaN | |
| 78 | test__subtf3(@bitCast(f128, (u128(0x7fff000000000000) << 64) | u128(0x800030000000)), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); | |
| 79 | ||
| 80 | // inf - any = inf | |
| 81 | test__subtf3(inf128, 0x1.23456789abcdefp+5, 0x7fff000000000000, 0x0); | |
| 82 | ||
| 83 | // any + any | |
| 84 | test__subtf3(0x1.234567829a3bcdef5678ade36734p+5, 0x1.ee9d7c52354a6936ab8d7654321fp-1, 0x40041b8af1915166, 0xa44a7bca780a166c); | |
| 85 | } |
std/special/compiler_rt/index.zig+4| ... | ... | @@ -21,6 +21,9 @@ comptime { |
| 21 | 21 | |
| 22 | 22 | @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); |
| 23 | 23 | |
| 24 | @export("__addtf3", @import("addXf3.zig").__addtf3, linkage); | |
| 25 | @export("__subtf3", @import("addXf3.zig").__subtf3, linkage); | |
| 26 | ||
| 24 | 27 | @export("__floattitf", @import("floattitf.zig").__floattitf, linkage); |
| 25 | 28 | @export("__floattidf", @import("floattidf.zig").__floattidf, linkage); |
| 26 | 29 | @export("__floattisf", @import("floattisf.zig").__floattisf, linkage); |
| ... | ... | @@ -37,6 +40,7 @@ comptime { |
| 37 | 40 | @export("__extendhfsf2", @import("extendXfYf2.zig").__extendhfsf2, linkage); |
| 38 | 41 | |
| 39 | 42 | @export("__truncsfhf2", @import("truncXfYf2.zig").__truncsfhf2, linkage); |
| 43 | @export("__truncdfhf2", @import("truncXfYf2.zig").__truncdfhf2, linkage); | |
| 40 | 44 | @export("__trunctfdf2", @import("truncXfYf2.zig").__trunctfdf2, linkage); |
| 41 | 45 | @export("__trunctfsf2", @import("truncXfYf2.zig").__trunctfsf2, linkage); |
| 42 | 46 |
std/special/compiler_rt/truncXfYf2.zig+4| ... | ... | @@ -4,6 +4,10 @@ pub extern fn __truncsfhf2(a: f32) u16 { |
| 4 | 4 | return @bitCast(u16, truncXfYf2(f16, f32, a)); |
| 5 | 5 | } |
| 6 | 6 | |
| 7 | pub extern fn __truncdfhf2(a: f64) u16 { | |
| 8 | return @bitCast(u16, truncXfYf2(f16, f64, a)); | |
| 9 | } | |
| 10 | ||
| 7 | 11 | pub extern fn __trunctfsf2(a: f128) f32 { |
| 8 | 12 | return truncXfYf2(f32, f128, a); |
| 9 | 13 | } |
std/special/compiler_rt/truncXfYf2_test.zig+68| ... | ... | @@ -63,6 +63,74 @@ test "truncsfhf2" { |
| 63 | 63 | test__truncsfhf2(0x33000000, 0x0000); // 0x1.0p-25 -> zero |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | const __truncdfhf2 = @import("truncXfYf2.zig").__truncdfhf2; | |
| 67 | ||
| 68 | fn test__truncdfhf2(a: f64, expected: u16) void { | |
| 69 | const rep = @bitCast(u16, __truncdfhf2(a)); | |
| 70 | ||
| 71 | if (rep == expected) { | |
| 72 | return; | |
| 73 | } | |
| 74 | // test other possible NaN representation(signal NaN) | |
| 75 | else if (expected == 0x7e00) { | |
| 76 | if ((rep & 0x7c00) == 0x7c00 and (rep & 0x3ff) > 0) { | |
| 77 | return; | |
| 78 | } | |
| 79 | } | |
| 80 | ||
| 81 | @panic("__truncdfhf2 test failure"); | |
| 82 | } | |
| 83 | ||
| 84 | fn test__truncdfhf2_raw(a: u64, expected: u16) void { | |
| 85 | const actual = __truncdfhf2(@bitCast(f64, a)); | |
| 86 | ||
| 87 | if (actual == expected) { | |
| 88 | return; | |
| 89 | } | |
| 90 | ||
| 91 | @panic("__truncdfhf2 test failure"); | |
| 92 | } | |
| 93 | ||
| 94 | test "truncdfhf2" { | |
| 95 | test__truncdfhf2_raw(0x7ff8000000000000, 0x7e00); // qNaN | |
| 96 | test__truncdfhf2_raw(0x7ff0000000008000, 0x7e00); // NaN | |
| 97 | ||
| 98 | test__truncdfhf2_raw(0x7ff0000000000000, 0x7c00); //inf | |
| 99 | test__truncdfhf2_raw(0xfff0000000000000, 0xfc00); // -inf | |
| 100 | ||
| 101 | test__truncdfhf2(0.0, 0x0); // zero | |
| 102 | test__truncdfhf2_raw(0x80000000 << 32, 0x8000); // -zero | |
| 103 | ||
| 104 | test__truncdfhf2(3.1415926535, 0x4248); | |
| 105 | test__truncdfhf2(-3.1415926535, 0xc248); | |
| 106 | ||
| 107 | test__truncdfhf2(0x1.987124876876324p+1000, 0x7c00); | |
| 108 | test__truncdfhf2(0x1.987124876876324p+12, 0x6e62); | |
| 109 | test__truncdfhf2(0x1.0p+0, 0x3c00); | |
| 110 | test__truncdfhf2(0x1.0p-14, 0x0400); | |
| 111 | ||
| 112 | // denormal | |
| 113 | test__truncdfhf2(0x1.0p-20, 0x0010); | |
| 114 | test__truncdfhf2(0x1.0p-24, 0x0001); | |
| 115 | test__truncdfhf2(-0x1.0p-24, 0x8001); | |
| 116 | test__truncdfhf2(0x1.5p-25, 0x0001); | |
| 117 | ||
| 118 | // and back to zero | |
| 119 | test__truncdfhf2(0x1.0p-25, 0x0000); | |
| 120 | test__truncdfhf2(-0x1.0p-25, 0x8000); | |
| 121 | ||
| 122 | // max (precise) | |
| 123 | test__truncdfhf2(65504.0, 0x7bff); | |
| 124 | ||
| 125 | // max (rounded) | |
| 126 | test__truncdfhf2(65519.0, 0x7bff); | |
| 127 | ||
| 128 | // max (to +inf) | |
| 129 | test__truncdfhf2(65520.0, 0x7c00); | |
| 130 | test__truncdfhf2(-65520.0, 0xfc00); | |
| 131 | test__truncdfhf2(65536.0, 0x7c00); | |
| 132 | } | |
| 133 | ||
| 66 | 134 | const __trunctfsf2 = @import("truncXfYf2.zig").__trunctfsf2; |
| 67 | 135 | |
| 68 | 136 | fn test__trunctfsf2(a: f128, expected: u32) void { |