| ... | @@ -4,7 +4,7 @@ const debug = std.debug; | ... | @@ -4,7 +4,7 @@ const debug = std.debug; |
| 4 | const assert = debug.assert; | 4 | const assert = debug.assert; |
| 5 | const mem = std.mem; | 5 | const mem = std.mem; |
| 6 | const builtin = @import("builtin"); | 6 | const builtin = @import("builtin"); |
| 7 | const errol3 = @import("errol/index.zig").errol3; | 7 | const errol = @import("errol/index.zig"); |
| 8 | | 8 | |
| 9 | const max_int_digits = 65; | 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,6 +22,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 22 | IntegerWidth, | 22 | IntegerWidth, |
| 23 | Float, | 23 | Float, |
| 24 | FloatWidth, | 24 | FloatWidth, |
| | 25 | FloatScientific, |
| | 26 | FloatScientificWidth, |
| 25 | Character, | 27 | Character, |
| 26 | Buf, | 28 | Buf, |
| 27 | BufWidth, | 29 | BufWidth, |
| ... | @@ -87,6 +89,9 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), | ... | @@ -87,6 +89,9 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 87 | 's' => { | 89 | 's' => { |
| 88 | state = State.Buf; | 90 | state = State.Buf; |
| 89 | }, | 91 | }, |
| | 92 | 'e' => { |
| | 93 | state = State.FloatScientific; |
| | 94 | }, |
| 90 | '.' => { | 95 | '.' => { |
| 91 | state = State.Float; | 96 | state = State.Float; |
| 92 | }, | 97 | }, |
| ... | @@ -133,9 +138,33 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), | ... | @@ -133,9 +138,33 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 133 | '0' ... '9' => {}, | 138 | '0' ... '9' => {}, |
| 134 | else => @compileError("Unexpected character in format string: " ++ []u8{c}), | 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 | State.Float => switch (c) { | 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 | next_arg += 1; | 168 | next_arg += 1; |
| 140 | state = State.Start; | 169 | state = State.Start; |
| 141 | start_index = i + 1; | 170 | start_index = i + 1; |
| ... | @@ -199,7 +228,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ | ... | @@ -199,7 +228,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ |
| 199 | return formatInt(value, 10, false, 0, context, Errors, output); | 228 | return formatInt(value, 10, false, 0, context, Errors, output); |
| 200 | }, | 229 | }, |
| 201 | builtin.TypeId.Float => { | 230 | builtin.TypeId.Float => { |
| 202 | return formatFloat(value, context, Errors, output); | 231 | return formatFloatScientific(value, null, context, Errors, output); |
| 203 | }, | 232 | }, |
| 204 | builtin.TypeId.Void => { | 233 | builtin.TypeId.Void => { |
| 205 | return output(context, "void"); | 234 | return output(context, "void"); |
| ... | @@ -257,81 +286,237 @@ pub fn formatBuf(buf: []const u8, width: usize, | ... | @@ -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 | var x = f64(value); | 293 | var x = f64(value); |
| 262 | | 294 | |
| 263 | // Errol doesn't handle these special cases. | 295 | // Errol doesn't handle these special cases. |
| 264 | if (math.isNan(x)) { | | |
| 265 | return output(context, "NaN"); | | |
| 266 | } | | |
| 267 | if (math.signbit(x)) { | 296 | if (math.signbit(x)) { |
| 268 | try output(context, "-"); | 297 | try output(context, "-"); |
| 269 | x = -x; | 298 | x = -x; |
| 270 | } | 299 | } |
| | 300 | |
| | 301 | if (math.isNan(x)) { |
| | 302 | return output(context, "nan"); |
| | 303 | } |
| 271 | if (math.isPositiveInf(x)) { | 304 | if (math.isPositiveInf(x)) { |
| 272 | return output(context, "Infinity"); | 305 | return output(context, "inf"); |
| 273 | } | 306 | } |
| 274 | if (x == 0.0) { | 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 | var buffer: [32]u8 = undefined; | 326 | var buffer: [32]u8 = undefined; |
| 279 | const float_decimal = errol3(x, buffer[0..]); | 327 | var float_decimal = errol.errol3(x, buffer[0..]); |
| 280 | try output(context, float_decimal.digits[0..1]); | 328 | |
| 281 | try output(context, "."); | 329 | if (maybe_precision) |precision| { |
| 282 | if (float_decimal.digits.len > 1) { | 330 | errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); |
| 283 | const num_digits = if (@typeOf(value) == f32) | 331 | |
| 284 | math.min(usize(9), float_decimal.digits.len) | 332 | try output(context, float_decimal.digits[0..1]); |
| 285 | else | 333 | |
| 286 | float_decimal.digits.len; | 334 | // {e0} case prints no `.` |
| 287 | try output(context, float_decimal.digits[1 .. num_digits]); | 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 | } else { | 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) { | 364 | try output(context, "e"); |
| 293 | try output(context, "e"); | 365 | const exp = float_decimal.exp - 1; |
| 294 | try formatInt(float_decimal.exp - 1, 10, false, 0, context, Errors, output); | 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 | var x = f64(value); | 385 | var x = f64(value); |
| 300 | | 386 | |
| 301 | // Errol doesn't handle these special cases. | 387 | // Errol doesn't handle these special cases. |
| 302 | if (math.isNan(x)) { | | |
| 303 | return output(context, "NaN"); | | |
| 304 | } | | |
| 305 | if (math.signbit(x)) { | 388 | if (math.signbit(x)) { |
| 306 | try output(context, "-"); | 389 | try output(context, "-"); |
| 307 | x = -x; | 390 | x = -x; |
| 308 | } | 391 | } |
| | 392 | |
| | 393 | if (math.isNan(x)) { |
| | 394 | return output(context, "nan"); |
| | 395 | } |
| 309 | if (math.isPositiveInf(x)) { | 396 | if (math.isPositiveInf(x)) { |
| 310 | return output(context, "Infinity"); | 397 | return output(context, "inf"); |
| 311 | } | 398 | } |
| 312 | if (x == 0.0) { | 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 | var buffer: [32]u8 = undefined; | 420 | var buffer: [32]u8 = undefined; |
| 317 | const float_decimal = errol3(x, buffer[0..]); | 421 | var float_decimal = errol.errol3(x, buffer[0..]); |
| 318 | | 422 | |
| 319 | const num_left_digits = if (float_decimal.exp > 0) usize(float_decimal.exp) else 1; | 423 | if (maybe_precision) |precision| { |
| 320 | | 424 | errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); |
| 321 | try output(context, float_decimal.digits[0 .. num_left_digits]); | 425 | |
| 322 | try output(context, "."); | 426 | // exp < 0 means the leading is always 0 as errol result is normalized. |
| 323 | if (float_decimal.digits.len > 1) { | 427 | var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0; |
| 324 | const num_valid_digtis = if (@typeOf(value) == f32) math.min(usize(7), float_decimal.digits.len) | 428 | |
| 325 | else | 429 | // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. |
| 326 | float_decimal.digits.len; | 430 | var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); |
| 327 | | 431 | |
| 328 | const num_right_digits = if (precision != 0) | 432 | if (num_digits_whole > 0) { |
| 329 | math.min(precision, (num_valid_digtis-num_left_digits)) | 433 | // We may have to zero pad, for instance 1e4 requires zero padding. |
| 330 | else | 434 | try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]); |
| 331 | num_valid_digtis - num_left_digits; | 435 | |
| 332 | try output(context, float_decimal.digits[num_left_digits .. (num_left_digits + num_right_digits)]); | 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 | } else { | 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 | |
| ... | @@ -594,70 +779,210 @@ test "fmt.format" { | ... | @@ -594,70 +779,210 @@ test "fmt.format" { |
| 594 | const result = try bufPrint(buf1[0..], "pointer: {}\n", &value); | 779 | const result = try bufPrint(buf1[0..], "pointer: {}\n", &value); |
| 595 | assert(mem.startsWith(u8, result, "pointer: Struct@")); | 780 | assert(mem.startsWith(u8, result, "pointer: Struct@")); |
| 596 | } | 781 | } |
| 597 | | 782 | { |
| 598 | // TODO get these tests passing in release modes | 783 | var buf1: [32]u8 = undefined; |
| 599 | // https://github.com/zig-lang/zig/issues/564 | 784 | const value: f32 = 1.34; |
| 600 | if (builtin.mode == builtin.Mode.Debug) { | 785 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); |
| 601 | { | 786 | assert(mem.eql(u8, result, "f32: 1.34000003e+00\n")); |
| 602 | var buf1: [32]u8 = undefined; | 787 | } |
| 603 | const value: f32 = 12.34; | 788 | { |
| 604 | const result = try bufPrint(buf1[0..], "f32: {}\n", value); | 789 | var buf1: [32]u8 = undefined; |
| 605 | assert(mem.eql(u8, result, "f32: 1.23400001e1\n")); | 790 | const value: f32 = 12.34; |
| 606 | } | 791 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); |
| 607 | { | 792 | assert(mem.eql(u8, result, "f32: 1.23400001e+01\n")); |
| 608 | var buf1: [32]u8 = undefined; | 793 | } |
| 609 | const value: f64 = -12.34e10; | 794 | { |
| 610 | const result = try bufPrint(buf1[0..], "f64: {}\n", value); | 795 | var buf1: [32]u8 = undefined; |
| 611 | assert(mem.eql(u8, result, "f64: -1.234e11\n")); | 796 | const value: f64 = -12.34e10; |
| 612 | } | 797 | const result = try bufPrint(buf1[0..], "f64: {e}\n", value); |
| 613 | { | 798 | assert(mem.eql(u8, result, "f64: -1.234e+11\n")); |
| 614 | var buf1: [32]u8 = undefined; | 799 | } |
| 615 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); | 800 | { |
| 616 | assert(mem.eql(u8, result, "f64: NaN\n")); | 801 | // This fails on release due to a minor rounding difference. |
| 617 | } | 802 | // --release-fast outputs 9.999960000000001e-40 vs. the expected. |
| 618 | { | 803 | if (builtin.mode == builtin.Mode.Debug) { |
| 619 | var buf1: [32]u8 = undefined; | | |
| 620 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); | | |
| 621 | assert(mem.eql(u8, result, "f64: Infinity\n")); | | |
| 622 | } | | |
| 623 | { | | |
| 624 | var buf1: [32]u8 = undefined; | | |
| 625 | const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); | | |
| 626 | assert(mem.eql(u8, result, "f64: -Infinity\n")); | | |
| 627 | } | | |
| 628 | { | | |
| 629 | var buf1: [32]u8 = undefined; | | |
| 630 | const value: f32 = 1.1234; | | |
| 631 | const result = try bufPrint(buf1[0..], "f32: {.1}\n", value); | | |
| 632 | assert(mem.eql(u8, result, "f32: 1.1\n")); | | |
| 633 | } | | |
| 634 | { | | |
| 635 | var buf1: [32]u8 = undefined; | | |
| 636 | const value: f32 = 1234.567; | | |
| 637 | const result = try bufPrint(buf1[0..], "f32: {.2}\n", value); | | |
| 638 | assert(mem.eql(u8, result, "f32: 1234.56\n")); | | |
| 639 | } | | |
| 640 | { | | |
| 641 | var buf1: [32]u8 = undefined; | | |
| 642 | const value: f32 = -11.1234; | | |
| 643 | const result = try bufPrint(buf1[0..], "f32: {.4}\n", value); | | |
| 644 | // -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")); | | |
| 647 | } | | |
| 648 | { | | |
| 649 | var buf1: [32]u8 = undefined; | | |
| 650 | const value: f32 = 91.12345; | | |
| 651 | const result = try bufPrint(buf1[0..], "f32: {.}\n", value); | | |
| 652 | assert(mem.eql(u8, result, "f32: 91.12345\n")); | | |
| 653 | } | | |
| 654 | { | | |
| 655 | var buf1: [32]u8 = undefined; | 804 | var buf1: [32]u8 = undefined; |
| 656 | const value: f64 = 91.12345678901235; | 805 | const value: f64 = 9.999960e-40; |
| 657 | const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); | 806 | const result = try bufPrint(buf1[0..], "f64: {e}\n", value); |
| 658 | assert(mem.eql(u8, result, "f64: 91.1234567890\n")); | 807 | assert(mem.eql(u8, result, "f64: 9.99996e-40\n")); |
| 659 | } | 808 | } |
| 660 | | 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")); |
| | 835 | } |
| | 836 | { |
| | 837 | var buf1: [32]u8 = undefined; |
| | 838 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); |
| | 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")); |
| | 845 | } |
| | 846 | { |
| | 847 | var buf1: [32]u8 = undefined; |
| | 848 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); |
| | 849 | assert(mem.eql(u8, result, "f64: inf\n")); |
| | 850 | } |
| | 851 | { |
| | 852 | var buf1: [32]u8 = undefined; |
| | 853 | const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); |
| | 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")); |
| | 861 | } |
| | 862 | { |
| | 863 | var buf1: [32]u8 = undefined; |
| | 864 | const value: f32 = 1.1234; |
| | 865 | const result = try bufPrint(buf1[0..], "f32: {.1}\n", value); |
| | 866 | assert(mem.eql(u8, result, "f32: 1.1\n")); |
| | 867 | } |
| | 868 | { |
| | 869 | var buf1: [32]u8 = undefined; |
| | 870 | const value: f32 = 1234.567; |
| | 871 | const result = try bufPrint(buf1[0..], "f32: {.2}\n", value); |
| | 872 | assert(mem.eql(u8, result, "f32: 1234.57\n")); |
| | 873 | } |
| | 874 | { |
| | 875 | var buf1: [32]u8 = undefined; |
| | 876 | const value: f32 = -11.1234; |
| | 877 | const result = try bufPrint(buf1[0..], "f32: {.4}\n", value); |
| | 878 | // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). |
| | 879 | // -11.12339... is rounded back up to -11.1234 |
| | 880 | assert(mem.eql(u8, result, "f32: -11.1234\n")); |
| | 881 | } |
| | 882 | { |
| | 883 | var buf1: [32]u8 = undefined; |
| | 884 | const value: f32 = 91.12345; |
| | 885 | const result = try bufPrint(buf1[0..], "f32: {.5}\n", value); |
| | 886 | assert(mem.eql(u8, result, "f32: 91.12345\n")); |
| | 887 | } |
| | 888 | { |
| | 889 | var buf1: [32]u8 = undefined; |
| | 890 | const value: f64 = 91.12345678901235; |
| | 891 | const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); |
| | 892 | assert(mem.eql(u8, result, "f64: 91.1234567890\n")); |
| | 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 | } |
| | 977 | // std-windows-x86_64-Debug-bare test case fails |
| | 978 | { |
| | 979 | // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 |
| | 980 | // also rounds to 630 so I'm inclined to believe libc is not |
| | 981 | // optimal here. |
| | 982 | var buf1: [32]u8 = undefined; |
| | 983 | const value: f64 = f64(@bitCast(f32, u32(1518338049))); |
| | 984 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); |
| | 985 | assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); |
| 661 | } | 986 | } |
| 662 | } | 987 | } |
| 663 | | 988 | |