@@ -134,7 +134,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
134
134
},
135
135
State .Float = > switch (c ) {
136
136
'}' = > {
137
- try formatFloatDecimal (args [next_arg ], 0 , context , Errors , output );
137
+ try formatFloatDecimal (args [next_arg ], null , context , Errors , output );
138
138
next_arg += 1 ;
139
139
state = State .Start ;
140
140
start_index = i + 1 ;
@@ -261,14 +261,14 @@ pub fn formatFloat(value: var, context: var, comptime Errors: type, output: fn(@
261
261
262
262
// Errol doesn't handle these special cases.
263
263
if (math .isNan (x )) {
264
- return output (context , "NaN " );
264
+ return output (context , "nan " );
265
265
}
266
266
if (math .signbit (x )) {
267
267
try output (context , "-" );
268
268
x = - x ;
269
269
}
270
270
if (math .isPositiveInf (x )) {
271
- return output (context , "Infinity " );
271
+ return output (context , "inf " );
272
272
}
273
273
if (x == 0.0 ) {
274
274
return output (context , "0.0" );
@@ -294,43 +294,117 @@ pub fn formatFloat(value: var, context: var, comptime Errors: type, output: fn(@
294
294
}
295
295
}
296
296
297
- pub fn formatFloatDecimal (value : var , precision : usize , context : var , comptime Errors : type , output : fn (@typeOf (context ), []const u8 )Errors ! void ) Errors ! void {
297
+ // Print a float of the format x.yyyyy where the number of y is specified by the precision argument.
298
+ // The default precision is 5, values are rounded to the nearest value according to the precision.
299
+ pub fn formatFloatDecimal (value : var , prec : ? usize , context : var , comptime Errors : type , output : fn (@typeOf (context ), []const u8 )Errors ! void ) Errors ! void {
298
300
var x = f64 (value );
301
+ const precision = prec ?? 5 ;
299
302
300
303
// Errol doesn't handle these special cases.
301
304
if (math .isNan (x )) {
302
- return output (context , "NaN " );
305
+ return output (context , "nan " );
303
306
}
307
+
304
308
if (math .signbit (x )) {
305
309
try output (context , "-" );
306
310
x = - x ;
307
311
}
312
+
308
313
if (math .isPositiveInf (x )) {
309
- return output (context , "Infinity " );
314
+ return output (context , "inf " );
310
315
}
316
+
311
317
if (x == 0.0 ) {
312
- return output (context , "0.0" );
318
+ try output (context , "0." );
319
+
320
+ var i : usize = 0 ;
321
+ while (i < precision ) : (i += 1 ) {
322
+ try output (context , "0" );
323
+ }
324
+ return ;
313
325
}
314
326
327
+ // non-special case, use errol3
315
328
var buffer : [32 ]u8 = undefined ;
316
- const float_decimal = errol3 (x , buffer [0.. ]);
329
+ var float_decimal = errol3 (x , buffer [0.. ]);
330
+
331
+ // Find the digit which will signify the round point and start rounding backwards.
332
+ const round_digit = if (float_decimal .exp >= 0 ) precision + usize (float_decimal .exp ) else precision ;
333
+ if (round_digit < float_decimal .digits .len and float_decimal .digits [round_digit ] - '0' >= 5 ) {
334
+ debug .assert (round_digit >= 0 );
335
+
336
+ var i = round_digit ;
337
+ while (true ) {
338
+ if (i == 0 ) {
339
+ // Rounded all the way past the start. This was of the form 9.999...
340
+ // If the exponent is positive we have an extra digit and handle by printing
341
+ // immediately to avoid reshuffling the buffer.
342
+ // If negative, increase exp to adjust the zero-padding.
343
+ if (float_decimal .exp > 0 ) {
344
+ try output (context , "1" );
345
+ } else {
346
+ float_decimal .exp += 1 ;
347
+ }
348
+ break ;
349
+ }
350
+
351
+ i -= 1 ;
352
+
353
+ const new_value = (float_decimal .digits [i ] - '0' + 1 ) % 10 ;
354
+ float_decimal .digits [i ] = new_value + '0' ;
355
+
356
+ // must continue rounding until non-9
357
+ if (new_value != 0 ) {
358
+ break ;
359
+ }
360
+ }
361
+ }
317
362
318
- const num_left_digits = if (float_decimal .exp > 0 ) usize (float_decimal .exp ) else 1 ;
363
+ // exp < 0 means the leading is always 0 as errol result is normalized.
364
+ const num_digits_whole = if (float_decimal .exp >= 0 ) usize (float_decimal .exp ) else 0 ;
365
+ if (num_digits_whole > 0 ) {
366
+ try output (context , float_decimal .digits [0 .. num_digits_whole ]);
367
+ } else {
368
+ try output (context , "0" );
369
+ }
370
+
371
+ // {.0} special case doesn't want a trailing '.'
372
+ if (precision == 0 ) {
373
+ return ;
374
+ }
319
375
320
- try output (context , float_decimal .digits [0 .. num_left_digits ]);
321
376
try output (context , "." );
322
- if (float_decimal .digits .len > 1 ) {
323
- const num_valid_digtis = if (@typeOf (value ) == f32 ) math .min (usize (7 ), float_decimal .digits .len )
324
- else
325
- float_decimal .digits .len ;
326
377
327
- const num_right_digits = if (precision != 0 )
328
- math .min (precision , (num_valid_digtis - num_left_digits ))
329
- else
330
- num_valid_digtis - num_left_digits ;
331
- try output (context , float_decimal .digits [num_left_digits .. (num_left_digits + num_right_digits )]);
378
+ // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's.
379
+ var printed : usize = 0 ;
380
+
381
+ // Zero-fill until we reach significant digits or run out of precision.
382
+ if (float_decimal .exp < 0 ) {
383
+ const zero_digit_count = usize (- float_decimal .exp );
384
+
385
+ var i : usize = 0 ;
386
+ while (i < zero_digit_count and i < precision ) : (i += 1 ) {
387
+ try output (context , "0" );
388
+ printed += 1 ;
389
+ }
390
+
391
+ if (i >= precision ) {
392
+ return ;
393
+ }
394
+ }
395
+
396
+ // Remaining fractional portion, zero-padding till desired precision.
397
+ const remaining_digits = float_decimal .digits .len - num_digits_whole ;
398
+ if (precision < remaining_digits ) {
399
+ try output (context , float_decimal .digits [num_digits_whole .. num_digits_whole + precision ]);
400
+ return ;
332
401
} else {
333
- try output (context , "0" );
402
+ try output (context , float_decimal .digits [num_digits_whole .. ]);
403
+ printed += float_decimal .digits .len - num_digits_whole ;
404
+
405
+ while (printed < precision ) : (printed += 1 ) {
406
+ try output (context , "0" );
407
+ }
334
408
}
335
409
}
336
410
@@ -612,17 +686,17 @@ test "fmt.format" {
612
686
{
613
687
var buf1 : [32 ]u8 = undefined ;
614
688
const result = try bufPrint (buf1 [0.. ], "f64: {}\n " , math .nan_f64 );
615
- assert (mem .eql (u8 , result , "f64: NaN \n " ));
689
+ assert (mem .eql (u8 , result , "f64: nan \n " ));
616
690
}
617
691
{
618
692
var buf1 : [32 ]u8 = undefined ;
619
693
const result = try bufPrint (buf1 [0.. ], "f64: {}\n " , math .inf_f64 );
620
- assert (mem .eql (u8 , result , "f64: Infinity \n " ));
694
+ assert (mem .eql (u8 , result , "f64: inf \n " ));
621
695
}
622
696
{
623
697
var buf1 : [32 ]u8 = undefined ;
624
698
const result = try bufPrint (buf1 [0.. ], "f64: {}\n " , - math .inf_f64 );
625
- assert (mem .eql (u8 , result , "f64: -Infinity \n " ));
699
+ assert (mem .eql (u8 , result , "f64: -inf \n " ));
626
700
}
627
701
{
628
702
var buf1 : [32 ]u8 = undefined ;
@@ -634,15 +708,15 @@ test "fmt.format" {
634
708
var buf1 : [32 ]u8 = undefined ;
635
709
const value : f32 = 1234.567 ;
636
710
const result = try bufPrint (buf1 [0.. ], "f32: {.2}\n " , value );
637
- assert (mem .eql (u8 , result , "f32: 1234.56 \n " ));
711
+ assert (mem .eql (u8 , result , "f32: 1234.57 \n " ));
638
712
}
639
713
{
640
714
var buf1 : [32 ]u8 = undefined ;
641
715
const value : f32 = -11.1234 ;
642
716
const result = try bufPrint (buf1 [0.. ], "f32: {.4}\n " , value );
643
717
// -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
644
- // -11.12339... is truncated to -11.1233
645
- assert (mem .eql (u8 , result , "f32: -11.1233 \n " ));
718
+ // -11.12339... is rounded back up to -11.1234
719
+ assert (mem .eql (u8 , result , "f32: -11.1234 \n " ));
646
720
}
647
721
{
648
722
var buf1 : [32 ]u8 = undefined ;
@@ -656,7 +730,48 @@ test "fmt.format" {
656
730
const result = try bufPrint (buf1 [0.. ], "f64: {.10}\n " , value );
657
731
assert (mem .eql (u8 , result , "f64: 91.1234567890\n " ));
658
732
}
659
-
733
+ {
734
+ var buf1 : [32 ]u8 = undefined ;
735
+ const value : f64 = 0.0 ;
736
+ const result = try bufPrint (buf1 [0.. ], "f64: {.5}\n " , value );
737
+ assert (mem .eql (u8 , result , "f64: 0.00000\n " ));
738
+ }
739
+ {
740
+ var buf1 : [32 ]u8 = undefined ;
741
+ const value : f64 = 5.700 ;
742
+ const result = try bufPrint (buf1 [0.. ], "f64: {.0}\n " , value );
743
+ assert (mem .eql (u8 , result , "f64: 6\n " ));
744
+ }
745
+ {
746
+ var buf1 : [32 ]u8 = undefined ;
747
+ const value : f64 = 9.999 ;
748
+ const result = try bufPrint (buf1 [0.. ], "f64: {.1}\n " , value );
749
+ assert (mem .eql (u8 , result , "f64: 10.0\n " ));
750
+ }
751
+ {
752
+ var buf1 : [32 ]u8 = undefined ;
753
+ const value : f64 = 1.0 ;
754
+ const result = try bufPrint (buf1 [0.. ], "f64: {.3}\n " , value );
755
+ assert (mem .eql (u8 , result , "f64: 1.000\n " ));
756
+ }
757
+ {
758
+ var buf1 : [32 ]u8 = undefined ;
759
+ const value : f64 = 0.0003 ;
760
+ const result = try bufPrint (buf1 [0.. ], "f64: {.8}\n " , value );
761
+ assert (mem .eql (u8 , result , "f64: 0.00030000\n " ));
762
+ }
763
+ {
764
+ var buf1 : [32 ]u8 = undefined ;
765
+ const value : f64 = 1.40130e-45 ;
766
+ const result = try bufPrint (buf1 [0.. ], "f64: {.5}\n " , value );
767
+ assert (mem .eql (u8 , result , "f64: 0.00000\n " ));
768
+ }
769
+ {
770
+ var buf1 : [32 ]u8 = undefined ;
771
+ const value : f64 = 9.999960e-40 ;
772
+ const result = try bufPrint (buf1 [0.. ], "f64: {.5}\n " , value );
773
+ assert (mem .eql (u8 , result , "f64: 0.00000\n " ));
774
+ }
660
775
}
661
776
}
662
777
0 commit comments