authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-28 22:43:52-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2018-04-28 22:43:52-04:00
log0bb054e5e7ccb164ea1649608d2f6e4195519cb2
tree7297e2951b28154090921bb93ebc6226b04f676d
parent73bf897b5cc25ee3f1ec9d0ba1483d779de4b7c3
parentec2a81a081f6c6d77de1bbeebf1d87754a4fae67
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #930 from zig-lang/float-printing

Finish and fix float printing

8 files changed, 554 insertions(+), 119 deletions(-)

std/fmt/errol/index.zig+76-3
...@@ -12,13 +12,79 @@ pub const FloatDecimal = struct {...@@ -12,13 +12,79 @@ pub const FloatDecimal = struct {
12 exp: i32,12 exp: i32,
13};13};
1414
15pub const RoundMode = enum {
16 // Round only the fractional portion (e.g. 1234.23 has precision 2)
17 Decimal,
18 // Round the entire whole/fractional portion (e.g. 1.23423e3 has precision 5)
19 Scientific,
20};
21
22/// Round a FloatDecimal as returned by errol3 to the specified fractional precision.
23/// All digits after the specified precision should be considered invalid.
24pub fn roundToPrecision(float_decimal: &FloatDecimal, precision: usize, mode: RoundMode) void {
25 // The round digit refers to the index which we should look at to determine
26 // whether we need to round to match the specified precision.
27 var round_digit: usize = 0;
28
29 switch (mode) {
30 RoundMode.Decimal => {
31 if (float_decimal.exp >= 0) {
32 round_digit = precision + usize(float_decimal.exp);
33 } else {
34 // if a small negative exp, then adjust we need to offset by the number
35 // of leading zeros that will occur.
36 const min_exp_required = usize(-float_decimal.exp);
37 if (precision > min_exp_required) {
38 round_digit = precision - min_exp_required;
39 }
40 }
41 },
42 RoundMode.Scientific => {
43 round_digit = 1 + precision;
44 },
45 }
46
47 // It suffices to look at just this digit. We don't round and propagate say 0.04999 to 0.05
48 // first, and then to 0.1 in the case of a {.1} single precision.
49
50 // Find the digit which will signify the round point and start rounding backwards.
51 if (round_digit < float_decimal.digits.len and float_decimal.digits[round_digit] - '0' >= 5) {
52 assert(round_digit >= 0);
53
54 var i = round_digit;
55 while (true) {
56 if (i == 0) {
57 // Rounded all the way past the start. This was of the form 9.999...
58 // Slot the new digit in place and increase the exponent.
59 float_decimal.exp += 1;
60
61 // Re-size the buffer to use the reserved leading byte.
62 const one_before = @intToPtr(&u8, @ptrToInt(&float_decimal.digits[0]) - 1);
63 float_decimal.digits = one_before[0..float_decimal.digits.len + 1];
64 float_decimal.digits[0] = '1';
65 return;
66 }
67
68 i -= 1;
69
70 const new_value = (float_decimal.digits[i] - '0' + 1) % 10;
71 float_decimal.digits[i] = new_value + '0';
72
73 // must continue rounding until non-9
74 if (new_value != 0) {
75 return;
76 }
77 }
78 }
79}
80
15/// Corrected Errol3 double to ASCII conversion.81/// Corrected Errol3 double to ASCII conversion.
16pub fn errol3(value: f64, buffer: []u8) FloatDecimal {82pub fn errol3(value: f64, buffer: []u8) FloatDecimal {
17 const bits = @bitCast(u64, value);83 const bits = @bitCast(u64, value);
18 const i = tableLowerBound(bits);84 const i = tableLowerBound(bits);
19 if (i < enum3.len and enum3[i] == bits) {85 if (i < enum3.len and enum3[i] == bits) {
20 const data = enum3_data[i];86 const data = enum3_data[i];
21 const digits = buffer[0..data.str.len];87 const digits = buffer[1..data.str.len + 1];
22 mem.copy(u8, digits, data.str);88 mem.copy(u8, digits, data.str);
23 return FloatDecimal {89 return FloatDecimal {
24 .digits = digits,90 .digits = digits,
...@@ -98,7 +164,11 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {...@@ -98,7 +164,11 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {
98 }164 }
99165
100 // digit generation166 // digit generation
101 var buf_index: usize = 0;167
168 // We generate digits starting at index 1. If rounding a buffer later then it may be
169 // required to generate a preceeding digit in some cases (9.999) in which case we use
170 // the 0-index for this extra digit.
171 var buf_index: usize = 1;
102 while (true) {172 while (true) {
103 var hdig = u8(math.floor(high.val));173 var hdig = u8(math.floor(high.val));
104 if ((high.val == f64(hdig)) and (high.off < 0))174 if ((high.val == f64(hdig)) and (high.off < 0))
...@@ -128,7 +198,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {...@@ -128,7 +198,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {
128 buf_index += 1;198 buf_index += 1;
129199
130 return FloatDecimal {200 return FloatDecimal {
131 .digits = buffer[0..buf_index],201 .digits = buffer[1..buf_index],
132 .exp = exp,202 .exp = exp,
133 };203 };
134}204}
...@@ -189,6 +259,9 @@ fn gethi(in: f64) f64 {...@@ -189,6 +259,9 @@ fn gethi(in: f64) f64 {
189/// Normalize the number by factoring in the error.259/// Normalize the number by factoring in the error.
190/// @hp: The float pair.260/// @hp: The float pair.
191fn hpNormalize(hp: &HP) void {261fn hpNormalize(hp: &HP) void {
262 // Required to avoid segfaults causing buffer overrun during errol3 digit output termination.
263 @setFloatMode(this, @import("builtin").FloatMode.Strict);
264
192 const val = hp.val;265 const val = hp.val;
193266
194 hp.val += hp.off;267 hp.val += hp.off;
std/fmt/index.zig+432-107
...@@ -4,7 +4,7 @@ const debug = std.debug;...@@ -4,7 +4,7 @@ const debug = std.debug;
4const assert = debug.assert;4const assert = debug.assert;
5const mem = std.mem;5const mem = std.mem;
6const builtin = @import("builtin");6const builtin = @import("builtin");
7const errol3 = @import("errol/index.zig").errol3;7const errol = @import("errol/index.zig");
88
9const max_int_digits = 65;9const max_int_digits = 65;
1010
...@@ -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}
259288
260pub 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.
292pub 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);
262294
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 }
277325
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 else333
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 }
291363
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}
297381
298pub 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).
384pub 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);
300386
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 }
315418
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..]);
318422
319 const num_left_digits = if (float_decimal.exp > 0) usize(float_decimal.exp) else 1;423 if (maybe_precision) |precision| {
320424 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 else429 // 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);
327431
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 else434 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}
337522
...@@ -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 }
597782 {
598 // TODO get these tests passing in release modes783 var buf1: [32]u8 = undefined;
599 // https://github.com/zig-lang/zig/issues/564784 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 }
660809 }
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}
663988
std/os/time.zig+1-1
...@@ -281,7 +281,7 @@ test "os.time.Timer" {...@@ -281,7 +281,7 @@ test "os.time.Timer" {
281 debug.assert(time_0 > 0 and time_0 < margin);281 debug.assert(time_0 > 0 and time_0 < margin);
282 282
283 const time_1 = timer.lap();283 const time_1 = timer.lap();
284 debug.assert(time_1 > time_0);284 debug.assert(time_1 >= time_0);
285 285
286 timer.reset();286 timer.reset();
287 debug.assert(timer.read() < time_1);287 debug.assert(timer.read() < time_1);
std/special/compiler_rt/index.zig+17-4
...@@ -32,10 +32,6 @@ comptime {...@@ -32,10 +32,6 @@ comptime {
32 @export("__fixunstfti", @import("fixunstfti.zig").__fixunstfti, linkage);32 @export("__fixunstfti", @import("fixunstfti.zig").__fixunstfti, linkage);
3333
34 @export("__udivmoddi4", @import("udivmoddi4.zig").__udivmoddi4, linkage);34 @export("__udivmoddi4", @import("udivmoddi4.zig").__udivmoddi4, linkage);
35 @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage);
36
37 @export("__udivti3", @import("udivti3.zig").__udivti3, linkage);
38 @export("__umodti3", @import("umodti3.zig").__umodti3, linkage);
3935
40 @export("__udivsi3", __udivsi3, linkage);36 @export("__udivsi3", __udivsi3, linkage);
41 @export("__udivdi3", __udivdi3, linkage);37 @export("__udivdi3", __udivdi3, linkage);
...@@ -62,9 +58,16 @@ comptime {...@@ -62,9 +58,16 @@ comptime {
62 @export("__chkstk", __chkstk, strong_linkage);58 @export("__chkstk", __chkstk, strong_linkage);
63 @export("___chkstk_ms", ___chkstk_ms, linkage);59 @export("___chkstk_ms", ___chkstk_ms, linkage);
64 }60 }
61 @export("__udivti3", @import("udivti3.zig").__udivti3_windows_x86_64, linkage);
62 @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4_windows_x86_64, linkage);
63 @export("__umodti3", @import("umodti3.zig").__umodti3_windows_x86_64, linkage);
65 },64 },
66 else => {},65 else => {},
67 }66 }
67 } else {
68 @export("__udivti3", @import("udivti3.zig").__udivti3, linkage);
69 @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage);
70 @export("__umodti3", @import("umodti3.zig").__umodti3, linkage);
68 }71 }
69}72}
7073
...@@ -83,6 +86,16 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn...@@ -83,6 +86,16 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn
83 }86 }
84}87}
8588
89pub fn setXmm0(comptime T: type, value: T) void {
90 comptime assert(builtin.arch == builtin.Arch.x86_64);
91 const aligned_value: T align(16) = value;
92 asm volatile (
93 \\movaps (%[ptr]), %%xmm0
94 :
95 : [ptr] "r" (&aligned_value)
96 : "xmm0");
97}
98
86extern fn __udivdi3(a: u64, b: u64) u64 {99extern fn __udivdi3(a: u64, b: u64) u64 {
87 @setRuntimeSafety(is_test);100 @setRuntimeSafety(is_test);
88 return __udivmoddi4(a, b, null);101 return __udivmoddi4(a, b, null);
std/special/compiler_rt/udivmodti4.zig+6
...@@ -1,11 +1,17 @@...@@ -1,11 +1,17 @@
1const udivmod = @import("udivmod.zig").udivmod;1const udivmod = @import("udivmod.zig").udivmod;
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const compiler_rt = @import("index.zig");
34
4pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) u128 {5pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) u128 {
5 @setRuntimeSafety(builtin.is_test);6 @setRuntimeSafety(builtin.is_test);
6 return udivmod(u128, a, b, maybe_rem);7 return udivmod(u128, a, b, maybe_rem);
7}8}
89
10pub extern fn __udivmodti4_windows_x86_64(a: &const u128, b: &const u128, maybe_rem: ?&u128) void {
11 @setRuntimeSafety(builtin.is_test);
12 compiler_rt.setXmm0(u128, udivmod(u128, *a, *b, maybe_rem));
13}
14
9test "import udivmodti4" {15test "import udivmodti4" {
10 _ = @import("udivmodti4_test.zig");16 _ = @import("udivmodti4_test.zig");
11}17}
std/special/compiler_rt/udivti3.zig+7-2
...@@ -1,7 +1,12 @@...@@ -1,7 +1,12 @@
1const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4;1const udivmodti4 = @import("udivmodti4.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub extern fn __udivti3(a: u128, b: u128) u128 {4pub extern fn __udivti3(a: u128, b: u128) u128 {
5 @setRuntimeSafety(builtin.is_test);5 @setRuntimeSafety(builtin.is_test);
6 return __udivmodti4(a, b, null);6 return udivmodti4.__udivmodti4(a, b, null);
7}
8
9pub extern fn __udivti3_windows_x86_64(a: &const u128, b: &const u128) void {
10 @setRuntimeSafety(builtin.is_test);
11 udivmodti4.__udivmodti4_windows_x86_64(a, b, null);
7}12}
std/special/compiler_rt/umodti3.zig+8-2
...@@ -1,9 +1,15 @@...@@ -1,9 +1,15 @@
1const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4;1const udivmodti4 = @import("udivmodti4.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const compiler_rt = @import("index.zig");
34
4pub extern fn __umodti3(a: u128, b: u128) u128 {5pub extern fn __umodti3(a: u128, b: u128) u128 {
5 @setRuntimeSafety(builtin.is_test);6 @setRuntimeSafety(builtin.is_test);
6 var r: u128 = undefined;7 var r: u128 = undefined;
7 _ = __udivmodti4(a, b, &r);8 _ = udivmodti4.__udivmodti4(a, b, &r);
8 return r;9 return r;
9}10}
11
12pub extern fn __umodti3_windows_x86_64(a: &const u128, b: &const u128) void {
13 @setRuntimeSafety(builtin.is_test);
14 compiler_rt.setXmm0(u128, __umodti3(*a, *b));
15}
test/cases/eval.zig+7
...@@ -529,3 +529,10 @@ test "comptime shlWithOverflow" {...@@ -529,3 +529,10 @@ test "comptime shlWithOverflow" {
529529
530 assert(ct_shifted == rt_shifted);530 assert(ct_shifted == rt_shifted);
531}531}
532
533test "runtime 128 bit integer division" {
534 var a: u128 = 152313999999999991610955792383;
535 var b: u128 = 10000000000000000000;
536 var c = a / b;
537 assert(c == 15231399999);
538}