| ... | ... | @@ -4,7 +4,7 @@ const debug = std.debug; |
| 4 | 4 | const assert = debug.assert; |
| 5 | 5 | const mem = std.mem; |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | | const errol3 = @import("errol/index.zig").errol3; |
| 7 | const errol = @import("errol/index.zig"); |
| 8 | 8 | |
| 9 | 9 | const max_int_digits = 65; |
| 10 | 10 | |
| ... | ... | @@ -22,6 +22,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 22 | 22 | IntegerWidth, |
| 23 | 23 | Float, |
| 24 | 24 | FloatWidth, |
| 25 | FloatScientific, |
| 26 | FloatScientificWidth, |
| 25 | 27 | Character, |
| 26 | 28 | Buf, |
| 27 | 29 | BufWidth, |
| ... | ... | @@ -87,6 +89,9 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 87 | 89 | 's' => { |
| 88 | 90 | state = State.Buf; |
| 89 | 91 | }, |
| 92 | 'e' => { |
| 93 | state = State.FloatScientific; |
| 94 | }, |
| 90 | 95 | '.' => { |
| 91 | 96 | state = State.Float; |
| 92 | 97 | }, |
| ... | ... | @@ -133,9 +138,33 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 133 | 138 | '0' ... '9' => {}, |
| 134 | 139 | else => @compileError("Unexpected character in format string: " ++ []u8{c}), |
| 135 | 140 | }, |
| 141 | State.FloatScientific => switch (c) { |
| 142 | '}' => { |
| 143 | try formatFloatScientific(args[next_arg], null, context, Errors, output); |
| 144 | next_arg += 1; |
| 145 | state = State.Start; |
| 146 | start_index = i + 1; |
| 147 | }, |
| 148 | '0' ... '9' => { |
| 149 | width_start = i; |
| 150 | state = State.FloatScientificWidth; |
| 151 | }, |
| 152 | else => @compileError("Unexpected character in format string: " ++ []u8{c}), |
| 153 | }, |
| 154 | State.FloatScientificWidth => switch (c) { |
| 155 | '}' => { |
| 156 | width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); |
| 157 | try formatFloatScientific(args[next_arg], width, context, Errors, output); |
| 158 | next_arg += 1; |
| 159 | state = State.Start; |
| 160 | start_index = i + 1; |
| 161 | }, |
| 162 | '0' ... '9' => {}, |
| 163 | else => @compileError("Unexpected character in format string: " ++ []u8{c}), |
| 164 | }, |
| 136 | 165 | State.Float => switch (c) { |
| 137 | 166 | '}' => { |
| 138 | | try formatFloatDecimal(args[next_arg], 0, context, Errors, output); |
| 167 | try formatFloatDecimal(args[next_arg], null, context, Errors, output); |
| 139 | 168 | next_arg += 1; |
| 140 | 169 | state = State.Start; |
| 141 | 170 | start_index = i + 1; |
| ... | ... | @@ -199,7 +228,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ |
| 199 | 228 | return formatInt(value, 10, false, 0, context, Errors, output); |
| 200 | 229 | }, |
| 201 | 230 | builtin.TypeId.Float => { |
| 202 | | return formatFloat(value, context, Errors, output); |
| 231 | return formatFloatScientific(value, null, context, Errors, output); |
| 203 | 232 | }, |
| 204 | 233 | builtin.TypeId.Void => { |
| 205 | 234 | return output(context, "void"); |
| ... | ... | @@ -257,81 +286,237 @@ pub fn formatBuf(buf: []const u8, width: usize, |
| 257 | 286 | } |
| 258 | 287 | } |
| 259 | 288 | |
| 260 | | pub fn formatFloat(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { |
| 289 | // Print a float in scientific notation to the specified precision. Null uses full precision. |
| 290 | // It should be the case that every full precision, printed value can be re-parsed back to the |
| 291 | // same type unambiguously. |
| 292 | pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { |
| 261 | 293 | var x = f64(value); |
| 262 | 294 | |
| 263 | 295 | // Errol doesn't handle these special cases. |
| 264 | | if (math.isNan(x)) { |
| 265 | | return output(context, "NaN"); |
| 266 | | } |
| 267 | 296 | if (math.signbit(x)) { |
| 268 | 297 | try output(context, "-"); |
| 269 | 298 | x = -x; |
| 270 | 299 | } |
| 300 | |
| 301 | if (math.isNan(x)) { |
| 302 | return output(context, "nan"); |
| 303 | } |
| 271 | 304 | if (math.isPositiveInf(x)) { |
| 272 | | return output(context, "Infinity"); |
| 305 | return output(context, "inf"); |
| 273 | 306 | } |
| 274 | 307 | if (x == 0.0) { |
| 275 | | return output(context, "0.0"); |
| 308 | try output(context, "0"); |
| 309 | |
| 310 | if (maybe_precision) |precision| { |
| 311 | if (precision != 0) { |
| 312 | try output(context, "."); |
| 313 | var i: usize = 0; |
| 314 | while (i < precision) : (i += 1) { |
| 315 | try output(context, "0"); |
| 316 | } |
| 317 | } |
| 318 | } else { |
| 319 | try output(context, ".0"); |
| 320 | } |
| 321 | |
| 322 | try output(context, "e+00"); |
| 323 | return; |
| 276 | 324 | } |
| 277 | 325 | |
| 278 | 326 | var buffer: [32]u8 = undefined; |
| 279 | | const float_decimal = errol3(x, buffer[0..]); |
| 280 | | try output(context, float_decimal.digits[0..1]); |
| 281 | | try output(context, "."); |
| 282 | | if (float_decimal.digits.len > 1) { |
| 283 | | const num_digits = if (@typeOf(value) == f32) |
| 284 | | math.min(usize(9), float_decimal.digits.len) |
| 285 | | else |
| 286 | | float_decimal.digits.len; |
| 287 | | try output(context, float_decimal.digits[1 .. num_digits]); |
| 327 | var float_decimal = errol.errol3(x, buffer[0..]); |
| 328 | |
| 329 | if (maybe_precision) |precision| { |
| 330 | errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); |
| 331 | |
| 332 | try output(context, float_decimal.digits[0..1]); |
| 333 | |
| 334 | // {e0} case prints no `.` |
| 335 | if (precision != 0) { |
| 336 | try output(context, "."); |
| 337 | |
| 338 | var printed: usize = 0; |
| 339 | if (float_decimal.digits.len > 1) { |
| 340 | const num_digits = math.min(float_decimal.digits.len, precision + 1); |
| 341 | try output(context, float_decimal.digits[1 .. num_digits]); |
| 342 | printed += num_digits - 1; |
| 343 | } |
| 344 | |
| 345 | while (printed < precision) : (printed += 1) { |
| 346 | try output(context, "0"); |
| 347 | } |
| 348 | } |
| 288 | 349 | } else { |
| 289 | | try output(context, "0"); |
| 350 | try output(context, float_decimal.digits[0..1]); |
| 351 | try output(context, "."); |
| 352 | if (float_decimal.digits.len > 1) { |
| 353 | const num_digits = if (@typeOf(value) == f32) |
| 354 | math.min(usize(9), float_decimal.digits.len) |
| 355 | else |
| 356 | float_decimal.digits.len; |
| 357 | |
| 358 | try output(context, float_decimal.digits[1 .. num_digits]); |
| 359 | } else { |
| 360 | try output(context, "0"); |
| 361 | } |
| 290 | 362 | } |
| 291 | 363 | |
| 292 | | if (float_decimal.exp != 1) { |
| 293 | | try output(context, "e"); |
| 294 | | try formatInt(float_decimal.exp - 1, 10, false, 0, context, Errors, output); |
| 364 | try output(context, "e"); |
| 365 | const exp = float_decimal.exp - 1; |
| 366 | |
| 367 | if (exp >= 0) { |
| 368 | try output(context, "+"); |
| 369 | if (exp > -10 and exp < 10) { |
| 370 | try output(context, "0"); |
| 371 | } |
| 372 | try formatInt(exp, 10, false, 0, context, Errors, output); |
| 373 | } else { |
| 374 | try output(context, "-"); |
| 375 | if (exp > -10 and exp < 10) { |
| 376 | try output(context, "0"); |
| 377 | } |
| 378 | try formatInt(-exp, 10, false, 0, context, Errors, output); |
| 295 | 379 | } |
| 296 | 380 | } |
| 297 | 381 | |
| 298 | | pub fn formatFloatDecimal(value: var, precision: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { |
| 382 | // Print a float of the format x.yyyyy where the number of y is specified by the precision argument. |
| 383 | // By default floats are printed at full precision (no rounding). |
| 384 | pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { |
| 299 | 385 | var x = f64(value); |
| 300 | 386 | |
| 301 | 387 | // Errol doesn't handle these special cases. |
| 302 | | if (math.isNan(x)) { |
| 303 | | return output(context, "NaN"); |
| 304 | | } |
| 305 | 388 | if (math.signbit(x)) { |
| 306 | 389 | try output(context, "-"); |
| 307 | 390 | x = -x; |
| 308 | 391 | } |
| 392 | |
| 393 | if (math.isNan(x)) { |
| 394 | return output(context, "nan"); |
| 395 | } |
| 309 | 396 | if (math.isPositiveInf(x)) { |
| 310 | | return output(context, "Infinity"); |
| 397 | return output(context, "inf"); |
| 311 | 398 | } |
| 312 | 399 | if (x == 0.0) { |
| 313 | | return output(context, "0.0"); |
| 400 | try output(context, "0"); |
| 401 | |
| 402 | if (maybe_precision) |precision| { |
| 403 | if (precision != 0) { |
| 404 | try output(context, "."); |
| 405 | var i: usize = 0; |
| 406 | while (i < precision) : (i += 1) { |
| 407 | try output(context, "0"); |
| 408 | } |
| 409 | } else { |
| 410 | try output(context, ".0"); |
| 411 | } |
| 412 | } else { |
| 413 | try output(context, "0"); |
| 414 | } |
| 415 | |
| 416 | return; |
| 314 | 417 | } |
| 315 | 418 | |
| 419 | // non-special case, use errol3 |
| 316 | 420 | var buffer: [32]u8 = undefined; |
| 317 | | const float_decimal = errol3(x, buffer[0..]); |
| 318 | | |
| 319 | | const num_left_digits = if (float_decimal.exp > 0) usize(float_decimal.exp) else 1; |
| 320 | | |
| 321 | | try output(context, float_decimal.digits[0 .. num_left_digits]); |
| 322 | | try output(context, "."); |
| 323 | | if (float_decimal.digits.len > 1) { |
| 324 | | const num_valid_digtis = if (@typeOf(value) == f32) math.min(usize(7), float_decimal.digits.len) |
| 325 | | else |
| 326 | | float_decimal.digits.len; |
| 327 | | |
| 328 | | const num_right_digits = if (precision != 0) |
| 329 | | math.min(precision, (num_valid_digtis-num_left_digits)) |
| 330 | | else |
| 331 | | num_valid_digtis - num_left_digits; |
| 332 | | try output(context, float_decimal.digits[num_left_digits .. (num_left_digits + num_right_digits)]); |
| 421 | var float_decimal = errol.errol3(x, buffer[0..]); |
| 422 | |
| 423 | if (maybe_precision) |precision| { |
| 424 | errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); |
| 425 | |
| 426 | // exp < 0 means the leading is always 0 as errol result is normalized. |
| 427 | var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0; |
| 428 | |
| 429 | // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. |
| 430 | var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); |
| 431 | |
| 432 | if (num_digits_whole > 0) { |
| 433 | // We may have to zero pad, for instance 1e4 requires zero padding. |
| 434 | try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]); |
| 435 | |
| 436 | var i = num_digits_whole_no_pad; |
| 437 | while (i < num_digits_whole) : (i += 1) { |
| 438 | try output(context, "0"); |
| 439 | } |
| 440 | } else { |
| 441 | try output(context , "0"); |
| 442 | } |
| 443 | |
| 444 | // {.0} special case doesn't want a trailing '.' |
| 445 | if (precision == 0) { |
| 446 | return; |
| 447 | } |
| 448 | |
| 449 | try output(context, "."); |
| 450 | |
| 451 | // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's. |
| 452 | var printed: usize = 0; |
| 453 | |
| 454 | // Zero-fill until we reach significant digits or run out of precision. |
| 455 | if (float_decimal.exp <= 0) { |
| 456 | const zero_digit_count = usize(-float_decimal.exp); |
| 457 | const zeros_to_print = math.min(zero_digit_count, precision); |
| 458 | |
| 459 | var i: usize = 0; |
| 460 | while (i < zeros_to_print) : (i += 1) { |
| 461 | try output(context, "0"); |
| 462 | printed += 1; |
| 463 | } |
| 464 | |
| 465 | if (printed >= precision) { |
| 466 | return; |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | // Remaining fractional portion, zero-padding if insufficient. |
| 471 | debug.assert(precision >= printed); |
| 472 | if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) { |
| 473 | try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); |
| 474 | return; |
| 475 | } else { |
| 476 | try output(context, float_decimal.digits[num_digits_whole_no_pad ..]); |
| 477 | printed += float_decimal.digits.len - num_digits_whole_no_pad; |
| 478 | |
| 479 | while (printed < precision) : (printed += 1) { |
| 480 | try output(context, "0"); |
| 481 | } |
| 482 | } |
| 333 | 483 | } else { |
| 334 | | try output(context, "0"); |
| 484 | // exp < 0 means the leading is always 0 as errol result is normalized. |
| 485 | var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0; |
| 486 | |
| 487 | // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. |
| 488 | var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); |
| 489 | |
| 490 | if (num_digits_whole > 0) { |
| 491 | // We may have to zero pad, for instance 1e4 requires zero padding. |
| 492 | try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]); |
| 493 | |
| 494 | var i = num_digits_whole_no_pad; |
| 495 | while (i < num_digits_whole) : (i += 1) { |
| 496 | try output(context, "0"); |
| 497 | } |
| 498 | } else { |
| 499 | try output(context , "0"); |
| 500 | } |
| 501 | |
| 502 | // Omit `.` if no fractional portion |
| 503 | if (float_decimal.exp >= 0 and num_digits_whole_no_pad == float_decimal.digits.len) { |
| 504 | return; |
| 505 | } |
| 506 | |
| 507 | try output(context, "."); |
| 508 | |
| 509 | // Zero-fill until we reach significant digits or run out of precision. |
| 510 | if (float_decimal.exp < 0) { |
| 511 | const zero_digit_count = usize(-float_decimal.exp); |
| 512 | |
| 513 | var i: usize = 0; |
| 514 | while (i < zero_digit_count) : (i += 1) { |
| 515 | try output(context, "0"); |
| 516 | } |
| 517 | } |
| 518 | |
| 519 | try output(context, float_decimal.digits[num_digits_whole_no_pad ..]); |
| 335 | 520 | } |
| 336 | 521 | } |
| 337 | 522 | |
| ... | ... | @@ -598,32 +783,81 @@ test "fmt.format" { |
| 598 | 783 | // TODO get these tests passing in release modes |
| 599 | 784 | // https://github.com/zig-lang/zig/issues/564 |
| 600 | 785 | if (builtin.mode == builtin.Mode.Debug) { |
| 786 | { |
| 787 | var buf1: [32]u8 = undefined; |
| 788 | const value: f32 = 1.34; |
| 789 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); |
| 790 | assert(mem.eql(u8, result, "f32: 1.34000003e+00\n")); |
| 791 | } |
| 601 | 792 | { |
| 602 | 793 | var buf1: [32]u8 = undefined; |
| 603 | 794 | const value: f32 = 12.34; |
| 604 | | const result = try bufPrint(buf1[0..], "f32: {}\n", value); |
| 605 | | assert(mem.eql(u8, result, "f32: 1.23400001e1\n")); |
| 795 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); |
| 796 | assert(mem.eql(u8, result, "f32: 1.23400001e+01\n")); |
| 606 | 797 | } |
| 607 | 798 | { |
| 608 | 799 | var buf1: [32]u8 = undefined; |
| 609 | 800 | const value: f64 = -12.34e10; |
| 610 | | const result = try bufPrint(buf1[0..], "f64: {}\n", value); |
| 611 | | assert(mem.eql(u8, result, "f64: -1.234e11\n")); |
| 801 | const result = try bufPrint(buf1[0..], "f64: {e}\n", value); |
| 802 | assert(mem.eql(u8, result, "f64: -1.234e+11\n")); |
| 803 | } |
| 804 | { |
| 805 | var buf1: [32]u8 = undefined; |
| 806 | const value: f64 = 9.999960e-40; |
| 807 | const result = try bufPrint(buf1[0..], "f64: {e}\n", value); |
| 808 | assert(mem.eql(u8, result, "f64: 9.99996e-40\n")); |
| 809 | } |
| 810 | { |
| 811 | var buf1: [32]u8 = undefined; |
| 812 | const value: f64 = 1.409706e-42; |
| 813 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); |
| 814 | assert(mem.eql(u8, result, "f64: 1.40971e-42\n")); |
| 815 | } |
| 816 | { |
| 817 | var buf1: [32]u8 = undefined; |
| 818 | const value: f64 = @bitCast(f32, u32(814313563)); |
| 819 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); |
| 820 | assert(mem.eql(u8, result, "f64: 1.00000e-09\n")); |
| 821 | } |
| 822 | { |
| 823 | var buf1: [32]u8 = undefined; |
| 824 | const value: f64 = @bitCast(f32, u32(1006632960)); |
| 825 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); |
| 826 | assert(mem.eql(u8, result, "f64: 7.81250e-03\n")); |
| 827 | } |
| 828 | { |
| 829 | // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. |
| 830 | // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. |
| 831 | var buf1: [32]u8 = undefined; |
| 832 | const value: f64 = @bitCast(f32, u32(1203982400)); |
| 833 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); |
| 834 | assert(mem.eql(u8, result, "f64: 1.00001e+05\n")); |
| 612 | 835 | } |
| 613 | 836 | { |
| 614 | 837 | var buf1: [32]u8 = undefined; |
| 615 | 838 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); |
| 616 | | assert(mem.eql(u8, result, "f64: NaN\n")); |
| 839 | assert(mem.eql(u8, result, "f64: nan\n")); |
| 840 | } |
| 841 | { |
| 842 | var buf1: [32]u8 = undefined; |
| 843 | const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64); |
| 844 | assert(mem.eql(u8, result, "f64: -nan\n")); |
| 617 | 845 | } |
| 618 | 846 | { |
| 619 | 847 | var buf1: [32]u8 = undefined; |
| 620 | 848 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); |
| 621 | | assert(mem.eql(u8, result, "f64: Infinity\n")); |
| 849 | assert(mem.eql(u8, result, "f64: inf\n")); |
| 622 | 850 | } |
| 623 | 851 | { |
| 624 | 852 | var buf1: [32]u8 = undefined; |
| 625 | 853 | const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); |
| 626 | | assert(mem.eql(u8, result, "f64: -Infinity\n")); |
| 854 | assert(mem.eql(u8, result, "f64: -inf\n")); |
| 855 | } |
| 856 | { |
| 857 | var buf1: [64]u8 = undefined; |
| 858 | const value: f64 = 1.52314e+29; |
| 859 | const result = try bufPrint(buf1[0..], "f64: {.}\n", value); |
| 860 | assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n")); |
| 627 | 861 | } |
| 628 | 862 | { |
| 629 | 863 | var buf1: [32]u8 = undefined; |
| ... | ... | @@ -635,20 +869,20 @@ test "fmt.format" { |
| 635 | 869 | var buf1: [32]u8 = undefined; |
| 636 | 870 | const value: f32 = 1234.567; |
| 637 | 871 | const result = try bufPrint(buf1[0..], "f32: {.2}\n", value); |
| 638 | | assert(mem.eql(u8, result, "f32: 1234.56\n")); |
| 872 | assert(mem.eql(u8, result, "f32: 1234.57\n")); |
| 639 | 873 | } |
| 640 | 874 | { |
| 641 | 875 | var buf1: [32]u8 = undefined; |
| 642 | 876 | const value: f32 = -11.1234; |
| 643 | 877 | const result = try bufPrint(buf1[0..], "f32: {.4}\n", value); |
| 644 | 878 | // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). |
| 645 | | // -11.12339... is truncated to -11.1233 |
| 646 | | assert(mem.eql(u8, result, "f32: -11.1233\n")); |
| 879 | // -11.12339... is rounded back up to -11.1234 |
| 880 | assert(mem.eql(u8, result, "f32: -11.1234\n")); |
| 647 | 881 | } |
| 648 | 882 | { |
| 649 | 883 | var buf1: [32]u8 = undefined; |
| 650 | 884 | const value: f32 = 91.12345; |
| 651 | | const result = try bufPrint(buf1[0..], "f32: {.}\n", value); |
| 885 | const result = try bufPrint(buf1[0..], "f32: {.5}\n", value); |
| 652 | 886 | assert(mem.eql(u8, result, "f32: 91.12345\n")); |
| 653 | 887 | } |
| 654 | 888 | { |
| ... | ... | @@ -657,7 +891,100 @@ test "fmt.format" { |
| 657 | 891 | const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); |
| 658 | 892 | assert(mem.eql(u8, result, "f64: 91.1234567890\n")); |
| 659 | 893 | } |
| 894 | { |
| 895 | var buf1: [32]u8 = undefined; |
| 896 | const value: f64 = 0.0; |
| 897 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); |
| 898 | assert(mem.eql(u8, result, "f64: 0.00000\n")); |
| 899 | } |
| 900 | { |
| 901 | var buf1: [32]u8 = undefined; |
| 902 | const value: f64 = 5.700; |
| 903 | const result = try bufPrint(buf1[0..], "f64: {.0}\n", value); |
| 904 | assert(mem.eql(u8, result, "f64: 6\n")); |
| 905 | } |
| 906 | { |
| 907 | var buf1: [32]u8 = undefined; |
| 908 | const value: f64 = 9.999; |
| 909 | const result = try bufPrint(buf1[0..], "f64: {.1}\n", value); |
| 910 | assert(mem.eql(u8, result, "f64: 10.0\n")); |
| 911 | } |
| 912 | { |
| 913 | var buf1: [32]u8 = undefined; |
| 914 | const value: f64 = 1.0; |
| 915 | const result = try bufPrint(buf1[0..], "f64: {.3}\n", value); |
| 916 | assert(mem.eql(u8, result, "f64: 1.000\n")); |
| 917 | } |
| 918 | { |
| 919 | var buf1: [32]u8 = undefined; |
| 920 | const value: f64 = 0.0003; |
| 921 | const result = try bufPrint(buf1[0..], "f64: {.8}\n", value); |
| 922 | assert(mem.eql(u8, result, "f64: 0.00030000\n")); |
| 923 | } |
| 924 | { |
| 925 | var buf1: [32]u8 = undefined; |
| 926 | const value: f64 = 1.40130e-45; |
| 927 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); |
| 928 | assert(mem.eql(u8, result, "f64: 0.00000\n")); |
| 929 | } |
| 930 | { |
| 931 | var buf1: [32]u8 = undefined; |
| 932 | const value: f64 = 9.999960e-40; |
| 933 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); |
| 934 | assert(mem.eql(u8, result, "f64: 0.00000\n")); |
| 935 | } |
| 936 | // libc checks |
| 937 | { |
| 938 | var buf1: [32]u8 = undefined; |
| 939 | const value: f64 = f64(@bitCast(f32, u32(916964781))); |
| 940 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); |
| 941 | assert(mem.eql(u8, result, "f64: 0.00001\n")); |
| 942 | } |
| 943 | { |
| 944 | var buf1: [32]u8 = undefined; |
| 945 | const value: f64 = f64(@bitCast(f32, u32(925353389))); |
| 946 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); |
| 947 | assert(mem.eql(u8, result, "f64: 0.00001\n")); |
| 948 | } |
| 949 | { |
| 950 | var buf1: [32]u8 = undefined; |
| 951 | const value: f64 = f64(@bitCast(f32, u32(1036831278))); |
| 952 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); |
| 953 | assert(mem.eql(u8, result, "f64: 0.10000\n")); |
| 954 | } |
| 955 | { |
| 956 | var buf1: [32]u8 = undefined; |
| 957 | const value: f64 = f64(@bitCast(f32, u32(1065353133))); |
| 958 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); |
| 959 | assert(mem.eql(u8, result, "f64: 1.00000\n")); |
| 960 | } |
| 961 | { |
| 962 | var buf1: [32]u8 = undefined; |
| 963 | const value: f64 = f64(@bitCast(f32, u32(1092616192))); |
| 964 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); |
| 965 | assert(mem.eql(u8, result, "f64: 10.00000\n")); |
| 966 | } |
| 967 | // libc differences |
| 968 | { |
| 969 | var buf1: [32]u8 = undefined; |
| 970 | // This is 0.015625 exactly according to gdb. We thus round down, |
| 971 | // however glibc rounds up for some reason. This occurs for all |
| 972 | // floats of the form x.yyyy25 on a precision point. |
| 973 | const value: f64 = f64(@bitCast(f32, u32(1015021568))); |
| 974 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); |
| 975 | assert(mem.eql(u8, result, "f64: 0.01563\n")); |
| 976 | } |
| 660 | 977 | |
| 978 | // std-windows-x86_64-Debug-bare test case fails |
| 979 | { |
| 980 | // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 |
| 981 | // also rounds to 630 so I'm inclined to believe libc is not |
| 982 | // optimal here. |
| 983 | var buf1: [32]u8 = undefined; |
| 984 | const value: f64 = f64(@bitCast(f32, u32(1518338049))); |
| 985 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); |
| 986 | assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); |
| 987 | } |
| 661 | 988 | } |
| 662 | 989 | } |
| 663 | 990 | |