| 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,6 +482,7 @@ set(ZIG_STD_FILES |
| 482 | "fmt/errol/index.zig" | 482 | "fmt/errol/index.zig" |
| 483 | "fmt/errol/lookup.zig" | 483 | "fmt/errol/lookup.zig" |
| 484 | "fmt/index.zig" | 484 | "fmt/index.zig" |
| 485 | "fmt/parse_float.zig" | ||
| 485 | "hash/adler.zig" | 486 | "hash/adler.zig" |
| 486 | "hash/crc.zig" | 487 | "hash/crc.zig" |
| 487 | "hash/fnv.zig" | 488 | "hash/fnv.zig" |
| ... | @@ -608,6 +609,7 @@ set(ZIG_STD_FILES | ... | @@ -608,6 +609,7 @@ set(ZIG_STD_FILES |
| 608 | "special/bootstrap_lib.zig" | 609 | "special/bootstrap_lib.zig" |
| 609 | "special/build_runner.zig" | 610 | "special/build_runner.zig" |
| 610 | "special/builtin.zig" | 611 | "special/builtin.zig" |
| 612 | "special/compiler_rt/addXf3.zig" | ||
| 611 | "special/compiler_rt/aulldiv.zig" | 613 | "special/compiler_rt/aulldiv.zig" |
| 612 | "special/compiler_rt/aullrem.zig" | 614 | "special/compiler_rt/aullrem.zig" |
| 613 | "special/compiler_rt/comparetf2.zig" | 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,7 +831,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned |
| 831 | return x; | 831 | return x; |
| 832 | } | 832 | } |
| 833 | 833 | ||
| 834 | test "parseUnsigned" { | 834 | test "fmt.parseUnsigned" { |
| 835 | testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); | 835 | testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); |
| 836 | testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); | 836 | testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); |
| 837 | testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10)); | 837 | testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10)); |
| ... | @@ -858,6 +858,12 @@ test "parseUnsigned" { | ... | @@ -858,6 +858,12 @@ test "parseUnsigned" { |
| 858 | testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16)); | 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 | pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { | 867 | pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { |
| 862 | const value = switch (c) { | 868 | const value = switch (c) { |
| 863 | '0'...'9' => c - '0', | 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,7 +1345,7 @@ pub const Parser = struct { |
| 1345 | return if (token.number_is_integer) | 1345 | return if (token.number_is_integer) |
| 1346 | Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } | 1346 | Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } |
| 1347 | else | 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,7 +1366,8 @@ test "json.parser.dynamic" { |
| 1366 | \\ }, | 1366 | \\ }, |
| 1367 | \\ "Animated" : false, | 1367 | \\ "Animated" : false, |
| 1368 | \\ "IDs": [116, 943, 234, 38793], | 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,4 +1396,7 @@ test "json.parser.dynamic" { |
| 1395 | 1396 | ||
| 1396 | const obj0 = array_of_object.Array.at(0).Object.get("n").?.value; | 1397 | const obj0 = array_of_object.Array.at(0).Object.get("n").?.value; |
| 1397 | testing.expect(mem.eql(u8, obj0.String, "m")); | 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,6 +14,7 @@ pub fn fabs(x: var) @typeOf(x) { |
| 14 | f16 => fabs16(x), | 14 | f16 => fabs16(x), |
| 15 | f32 => fabs32(x), | 15 | f32 => fabs32(x), |
| 16 | f64 => fabs64(x), | 16 | f64 => fabs64(x), |
| 17 | f128 => fabs128(x), | ||
| 17 | else => @compileError("fabs not implemented for " ++ @typeName(T)), | 18 | else => @compileError("fabs not implemented for " ++ @typeName(T)), |
| 18 | }; | 19 | }; |
| 19 | } | 20 | } |
| ... | @@ -36,10 +37,17 @@ fn fabs64(x: f64) f64 { | ... | @@ -36,10 +37,17 @@ fn fabs64(x: f64) f64 { |
| 36 | return @bitCast(f64, u); | 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 | test "math.fabs" { | 46 | test "math.fabs" { |
| 40 | expect(fabs(f16(1.0)) == fabs16(1.0)); | 47 | expect(fabs(f16(1.0)) == fabs16(1.0)); |
| 41 | expect(fabs(f32(1.0)) == fabs32(1.0)); | 48 | expect(fabs(f32(1.0)) == fabs32(1.0)); |
| 42 | expect(fabs(f64(1.0)) == fabs64(1.0)); | 49 | expect(fabs(f64(1.0)) == fabs64(1.0)); |
| 50 | expect(fabs(f128(1.0)) == fabs128(1.0)); | ||
| 43 | } | 51 | } |
| 44 | 52 | ||
| 45 | test "math.fabs16" { | 53 | test "math.fabs16" { |
| ... | @@ -57,6 +65,11 @@ test "math.fabs64" { | ... | @@ -57,6 +65,11 @@ test "math.fabs64" { |
| 57 | expect(fabs64(-1.0) == 1.0); | 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 | test "math.fabs16.special" { | 73 | test "math.fabs16.special" { |
| 61 | expect(math.isPositiveInf(fabs(math.inf(f16)))); | 74 | expect(math.isPositiveInf(fabs(math.inf(f16)))); |
| 62 | expect(math.isPositiveInf(fabs(-math.inf(f16)))); | 75 | expect(math.isPositiveInf(fabs(-math.inf(f16)))); |
| ... | @@ -74,3 +87,9 @@ test "math.fabs64.special" { | ... | @@ -74,3 +87,9 @@ test "math.fabs64.special" { |
| 74 | expect(math.isPositiveInf(fabs(-math.inf(f64)))); | 87 | expect(math.isPositiveInf(fabs(-math.inf(f64)))); |
| 75 | expect(math.isNan(fabs(math.nan(f64)))); | 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,6 +51,12 @@ pub const nan_f64 = @bitCast(f64, nan_u64); |
| 51 | pub const inf_u64 = u64(0x7FF << 52); | 51 | pub const inf_u64 = u64(0x7FF << 52); |
| 52 | pub const inf_f64 = @bitCast(f64, inf_u64); | 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 | pub const nan = @import("nan.zig").nan; | 60 | pub const nan = @import("nan.zig").nan; |
| 55 | pub const snan = @import("nan.zig").snan; | 61 | pub const snan = @import("nan.zig").snan; |
| 56 | pub const inf = @import("inf.zig").inf; | 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,7 +385,7 @@ pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) t |
| 379 | return u0; | 385 | return u0; |
| 380 | } | 386 | } |
| 381 | const is_signed = from < 0; | 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 | const base = log2(largest_positive_integer); | 389 | const base = log2(largest_positive_integer); |
| 384 | const upper = (1 << base) - 1; | 390 | const upper = (1 << base) - 1; |
| 385 | var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1; | 391 | var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1; |
| ... | @@ -752,6 +758,7 @@ test "minInt and maxInt" { | ... | @@ -752,6 +758,7 @@ test "minInt and maxInt" { |
| 752 | testing.expect(maxInt(u16) == 65535); | 758 | testing.expect(maxInt(u16) == 65535); |
| 753 | testing.expect(maxInt(u32) == 4294967295); | 759 | testing.expect(maxInt(u32) == 4294967295); |
| 754 | testing.expect(maxInt(u64) == 18446744073709551615); | 760 | testing.expect(maxInt(u64) == 18446744073709551615); |
| 761 | testing.expect(maxInt(u128) == 340282366920938463463374607431768211455); | ||
| 755 | 762 | ||
| 756 | testing.expect(maxInt(i0) == 0); | 763 | testing.expect(maxInt(i0) == 0); |
| 757 | testing.expect(maxInt(i1) == 0); | 764 | testing.expect(maxInt(i1) == 0); |
| ... | @@ -760,6 +767,7 @@ test "minInt and maxInt" { | ... | @@ -760,6 +767,7 @@ test "minInt and maxInt" { |
| 760 | testing.expect(maxInt(i32) == 2147483647); | 767 | testing.expect(maxInt(i32) == 2147483647); |
| 761 | testing.expect(maxInt(i63) == 4611686018427387903); | 768 | testing.expect(maxInt(i63) == 4611686018427387903); |
| 762 | testing.expect(maxInt(i64) == 9223372036854775807); | 769 | testing.expect(maxInt(i64) == 9223372036854775807); |
| 770 | testing.expect(maxInt(i128) == 170141183460469231731687303715884105727); | ||
| 763 | 771 | ||
| 764 | testing.expect(minInt(u0) == 0); | 772 | testing.expect(minInt(u0) == 0); |
| 765 | testing.expect(minInt(u1) == 0); | 773 | testing.expect(minInt(u1) == 0); |
| ... | @@ -768,6 +776,7 @@ test "minInt and maxInt" { | ... | @@ -768,6 +776,7 @@ test "minInt and maxInt" { |
| 768 | testing.expect(minInt(u32) == 0); | 776 | testing.expect(minInt(u32) == 0); |
| 769 | testing.expect(minInt(u63) == 0); | 777 | testing.expect(minInt(u63) == 0); |
| 770 | testing.expect(minInt(u64) == 0); | 778 | testing.expect(minInt(u64) == 0); |
| 779 | testing.expect(minInt(u128) == 0); | ||
| 771 | 780 | ||
| 772 | testing.expect(minInt(i0) == 0); | 781 | testing.expect(minInt(i0) == 0); |
| 773 | testing.expect(minInt(i1) == -1); | 782 | testing.expect(minInt(i1) == -1); |
| ... | @@ -776,6 +785,7 @@ test "minInt and maxInt" { | ... | @@ -776,6 +785,7 @@ test "minInt and maxInt" { |
| 776 | testing.expect(minInt(i32) == -2147483648); | 785 | testing.expect(minInt(i32) == -2147483648); |
| 777 | testing.expect(minInt(i63) == -4611686018427387904); | 786 | testing.expect(minInt(i63) == -4611686018427387904); |
| 778 | testing.expect(minInt(i64) == -9223372036854775808); | 787 | testing.expect(minInt(i64) == -9223372036854775808); |
| 788 | testing.expect(minInt(i128) == -170141183460469231731687303715884105728); | ||
| 779 | } | 789 | } |
| 780 | 790 | ||
| 781 | test "max value type" { | 791 | test "max value type" { |
std/math/inf.zig+4-3| ... | @@ -3,9 +3,10 @@ const math = std.math; | ... | @@ -3,9 +3,10 @@ const math = std.math; |
| 3 | 3 | ||
| 4 | pub fn inf(comptime T: type) T { | 4 | pub fn inf(comptime T: type) T { |
| 5 | return switch (T) { | 5 | return switch (T) { |
| 6 | f16 => @bitCast(f16, math.inf_u16), | 6 | f16 => math.inf_f16, |
| 7 | f32 => @bitCast(f32, math.inf_u32), | 7 | f32 => math.inf_f32, |
| 8 | f64 => @bitCast(f64, math.inf_u64), | 8 | f64 => math.inf_f64, |
| 9 | f128 => math.inf_f128, | ||
| 9 | else => @compileError("inf not implemented for " ++ @typeName(T)), | 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,6 +18,10 @@ pub fn isInf(x: var) bool { |
| 18 | const bits = @bitCast(u64, x); | 18 | const bits = @bitCast(u64, x); |
| 19 | return bits & (maxInt(u64) >> 1) == (0x7FF << 52); | 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 | else => { | 25 | else => { |
| 22 | @compileError("isInf not implemented for " ++ @typeName(T)); | 26 | @compileError("isInf not implemented for " ++ @typeName(T)); |
| 23 | }, | 27 | }, |
| ... | @@ -36,6 +40,9 @@ pub fn isPositiveInf(x: var) bool { | ... | @@ -36,6 +40,9 @@ pub fn isPositiveInf(x: var) bool { |
| 36 | f64 => { | 40 | f64 => { |
| 37 | return @bitCast(u64, x) == 0x7FF << 52; | 41 | return @bitCast(u64, x) == 0x7FF << 52; |
| 38 | }, | 42 | }, |
| 43 | f128 => { | ||
| 44 | return @bitCast(u128, x) == 0x7FFF << 112; | ||
| 45 | }, | ||
| 39 | else => { | 46 | else => { |
| 40 | @compileError("isPositiveInf not implemented for " ++ @typeName(T)); | 47 | @compileError("isPositiveInf not implemented for " ++ @typeName(T)); |
| 41 | }, | 48 | }, |
| ... | @@ -54,6 +61,9 @@ pub fn isNegativeInf(x: var) bool { | ... | @@ -54,6 +61,9 @@ pub fn isNegativeInf(x: var) bool { |
| 54 | f64 => { | 61 | f64 => { |
| 55 | return @bitCast(u64, x) == 0xFFF << 52; | 62 | return @bitCast(u64, x) == 0xFFF << 52; |
| 56 | }, | 63 | }, |
| 64 | f128 => { | ||
| 65 | return @bitCast(u128, x) == 0xFFFF << 112; | ||
| 66 | }, | ||
| 57 | else => { | 67 | else => { |
| 58 | @compileError("isNegativeInf not implemented for " ++ @typeName(T)); | 68 | @compileError("isNegativeInf not implemented for " ++ @typeName(T)); |
| 59 | }, | 69 | }, |
| ... | @@ -67,12 +77,16 @@ test "math.isInf" { | ... | @@ -67,12 +77,16 @@ test "math.isInf" { |
| 67 | expect(!isInf(f32(-0.0))); | 77 | expect(!isInf(f32(-0.0))); |
| 68 | expect(!isInf(f64(0.0))); | 78 | expect(!isInf(f64(0.0))); |
| 69 | expect(!isInf(f64(-0.0))); | 79 | expect(!isInf(f64(-0.0))); |
| 80 | expect(!isInf(f128(0.0))); | ||
| 81 | expect(!isInf(f128(-0.0))); | ||
| 70 | expect(isInf(math.inf(f16))); | 82 | expect(isInf(math.inf(f16))); |
| 71 | expect(isInf(-math.inf(f16))); | 83 | expect(isInf(-math.inf(f16))); |
| 72 | expect(isInf(math.inf(f32))); | 84 | expect(isInf(math.inf(f32))); |
| 73 | expect(isInf(-math.inf(f32))); | 85 | expect(isInf(-math.inf(f32))); |
| 74 | expect(isInf(math.inf(f64))); | 86 | expect(isInf(math.inf(f64))); |
| 75 | expect(isInf(-math.inf(f64))); | 87 | expect(isInf(-math.inf(f64))); |
| 88 | expect(isInf(math.inf(f128))); | ||
| 89 | expect(isInf(-math.inf(f128))); | ||
| 76 | } | 90 | } |
| 77 | 91 | ||
| 78 | test "math.isPositiveInf" { | 92 | test "math.isPositiveInf" { |
| ... | @@ -82,12 +96,16 @@ test "math.isPositiveInf" { | ... | @@ -82,12 +96,16 @@ test "math.isPositiveInf" { |
| 82 | expect(!isPositiveInf(f32(-0.0))); | 96 | expect(!isPositiveInf(f32(-0.0))); |
| 83 | expect(!isPositiveInf(f64(0.0))); | 97 | expect(!isPositiveInf(f64(0.0))); |
| 84 | expect(!isPositiveInf(f64(-0.0))); | 98 | expect(!isPositiveInf(f64(-0.0))); |
| 99 | expect(!isPositiveInf(f128(0.0))); | ||
| 100 | expect(!isPositiveInf(f128(-0.0))); | ||
| 85 | expect(isPositiveInf(math.inf(f16))); | 101 | expect(isPositiveInf(math.inf(f16))); |
| 86 | expect(!isPositiveInf(-math.inf(f16))); | 102 | expect(!isPositiveInf(-math.inf(f16))); |
| 87 | expect(isPositiveInf(math.inf(f32))); | 103 | expect(isPositiveInf(math.inf(f32))); |
| 88 | expect(!isPositiveInf(-math.inf(f32))); | 104 | expect(!isPositiveInf(-math.inf(f32))); |
| 89 | expect(isPositiveInf(math.inf(f64))); | 105 | expect(isPositiveInf(math.inf(f64))); |
| 90 | expect(!isPositiveInf(-math.inf(f64))); | 106 | expect(!isPositiveInf(-math.inf(f64))); |
| 107 | expect(isPositiveInf(math.inf(f128))); | ||
| 108 | expect(!isPositiveInf(-math.inf(f128))); | ||
| 91 | } | 109 | } |
| 92 | 110 | ||
| 93 | test "math.isNegativeInf" { | 111 | test "math.isNegativeInf" { |
| ... | @@ -97,10 +115,14 @@ test "math.isNegativeInf" { | ... | @@ -97,10 +115,14 @@ test "math.isNegativeInf" { |
| 97 | expect(!isNegativeInf(f32(-0.0))); | 115 | expect(!isNegativeInf(f32(-0.0))); |
| 98 | expect(!isNegativeInf(f64(0.0))); | 116 | expect(!isNegativeInf(f64(0.0))); |
| 99 | expect(!isNegativeInf(f64(-0.0))); | 117 | expect(!isNegativeInf(f64(-0.0))); |
| 118 | expect(!isNegativeInf(f128(0.0))); | ||
| 119 | expect(!isNegativeInf(f128(-0.0))); | ||
| 100 | expect(!isNegativeInf(math.inf(f16))); | 120 | expect(!isNegativeInf(math.inf(f16))); |
| 101 | expect(isNegativeInf(-math.inf(f16))); | 121 | expect(isNegativeInf(-math.inf(f16))); |
| 102 | expect(!isNegativeInf(math.inf(f32))); | 122 | expect(!isNegativeInf(math.inf(f32))); |
| 103 | expect(isNegativeInf(-math.inf(f32))); | 123 | expect(isNegativeInf(-math.inf(f32))); |
| 104 | expect(!isNegativeInf(math.inf(f64))); | 124 | expect(!isNegativeInf(math.inf(f64))); |
| 105 | expect(isNegativeInf(-math.inf(f64))); | 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,6 +18,10 @@ pub fn isNan(x: var) bool { |
| 18 | const bits = @bitCast(u64, x); | 18 | const bits = @bitCast(u64, x); |
| 19 | return (bits & (maxInt(u64) >> 1)) > (u64(0x7FF) << 52); | 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 | else => { | 25 | else => { |
| 22 | @compileError("isNan not implemented for " ++ @typeName(T)); | 26 | @compileError("isNan not implemented for " ++ @typeName(T)); |
| 23 | }, | 27 | }, |
| ... | @@ -34,7 +38,9 @@ test "math.isNan" { | ... | @@ -34,7 +38,9 @@ test "math.isNan" { |
| 34 | expect(isNan(math.nan(f16))); | 38 | expect(isNan(math.nan(f16))); |
| 35 | expect(isNan(math.nan(f32))); | 39 | expect(isNan(math.nan(f32))); |
| 36 | expect(isNan(math.nan(f64))); | 40 | expect(isNan(math.nan(f64))); |
| 41 | expect(isNan(math.nan(f128))); | ||
| 37 | expect(!isNan(f16(1.0))); | 42 | expect(!isNan(f16(1.0))); |
| 38 | expect(!isNan(f32(1.0))); | 43 | expect(!isNan(f32(1.0))); |
| 39 | expect(!isNan(f64(1.0))); | 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,9 +2,10 @@ const math = @import("index.zig"); |
| 2 | 2 | ||
| 3 | pub fn nan(comptime T: type) T { | 3 | pub fn nan(comptime T: type) T { |
| 4 | return switch (T) { | 4 | return switch (T) { |
| 5 | f16 => @bitCast(f16, math.nan_u16), | 5 | f16 => math.nan_f16, |
| 6 | f32 => @bitCast(f32, math.nan_u32), | 6 | f32 => math.nan_f32, |
| 7 | f64 => @bitCast(f64, math.nan_u64), | 7 | f64 => math.nan_f64, |
| 8 | f128 => math.nan_f128, | ||
| 8 | else => @compileError("nan not implemented for " ++ @typeName(T)), | 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,6 +21,9 @@ comptime { |
| 21 | 21 | ||
| 22 | @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); | 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 | @export("__floattitf", @import("floattitf.zig").__floattitf, linkage); | 27 | @export("__floattitf", @import("floattitf.zig").__floattitf, linkage); |
| 25 | @export("__floattidf", @import("floattidf.zig").__floattidf, linkage); | 28 | @export("__floattidf", @import("floattidf.zig").__floattidf, linkage); |
| 26 | @export("__floattisf", @import("floattisf.zig").__floattisf, linkage); | 29 | @export("__floattisf", @import("floattisf.zig").__floattisf, linkage); |
| ... | @@ -37,6 +40,7 @@ comptime { | ... | @@ -37,6 +40,7 @@ comptime { |
| 37 | @export("__extendhfsf2", @import("extendXfYf2.zig").__extendhfsf2, linkage); | 40 | @export("__extendhfsf2", @import("extendXfYf2.zig").__extendhfsf2, linkage); |
| 38 | 41 | ||
| 39 | @export("__truncsfhf2", @import("truncXfYf2.zig").__truncsfhf2, linkage); | 42 | @export("__truncsfhf2", @import("truncXfYf2.zig").__truncsfhf2, linkage); |
| 43 | @export("__truncdfhf2", @import("truncXfYf2.zig").__truncdfhf2, linkage); | ||
| 40 | @export("__trunctfdf2", @import("truncXfYf2.zig").__trunctfdf2, linkage); | 44 | @export("__trunctfdf2", @import("truncXfYf2.zig").__trunctfdf2, linkage); |
| 41 | @export("__trunctfsf2", @import("truncXfYf2.zig").__trunctfsf2, linkage); | 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,6 +4,10 @@ pub extern fn __truncsfhf2(a: f32) u16 { |
| 4 | return @bitCast(u16, truncXfYf2(f16, f32, a)); | 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 | pub extern fn __trunctfsf2(a: f128) f32 { | 11 | pub extern fn __trunctfsf2(a: f128) f32 { |
| 8 | return truncXfYf2(f32, f128, a); | 12 | return truncXfYf2(f32, f128, a); |
| 9 | } | 13 | } |
std/special/compiler_rt/truncXfYf2_test.zig+68| ... | @@ -63,6 +63,74 @@ test "truncsfhf2" { | ... | @@ -63,6 +63,74 @@ test "truncsfhf2" { |
| 63 | test__truncsfhf2(0x33000000, 0x0000); // 0x1.0p-25 -> zero | 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 | const __trunctfsf2 = @import("truncXfYf2.zig").__trunctfsf2; | 134 | const __trunctfsf2 = @import("truncXfYf2.zig").__trunctfsf2; |
| 67 | 135 | ||
| 68 | fn test__trunctfsf2(a: f128, expected: u32) void { | 136 | fn test__trunctfsf2(a: f128, expected: u32) void { |