@@ -131,8 +131,8 @@ int Library_corlib_native_System_Number::DoPrintfOnDataType(char *buffer, char *
131
131
132
132
const char *Library_corlib_native_System_Number::GetPrintfLengthModifier (CLR_DataType dataType)
133
133
{
134
- const char *ret = (dataType == DATATYPE_I1 || dataType == DATATYPE_U1) ? " hh "
135
- : (dataType == DATATYPE_I2 || dataType == DATATYPE_U2) ? " h "
134
+ const char *ret = (dataType == DATATYPE_I1 || dataType == DATATYPE_U1) ? " "
135
+ : (dataType == DATATYPE_I2 || dataType == DATATYPE_U2) ? " "
136
136
: (dataType == DATATYPE_I4 || dataType == DATATYPE_U4) ? " "
137
137
: (dataType == DATATYPE_I8 || dataType == DATATYPE_U8) ? " ll"
138
138
: " " ;
@@ -328,9 +328,6 @@ int Library_corlib_native_System_Number::Format_G(
328
328
329
329
bool isIntegerDataType = IsIntegerDataType (dataType);
330
330
331
- // flag to indicate if we're forcing extra precision for the conversion
332
- bool forcedPrecision = true ;
333
-
334
331
// set default precision for the conversion
335
332
int defaultPrecision = 0 ;
336
333
switch (dataType)
@@ -354,37 +351,43 @@ int Library_corlib_native_System_Number::Format_G(
354
351
defaultPrecision = 20 ;
355
352
break ;
356
353
case DATATYPE_R4:
357
- defaultPrecision = 7 ;
354
+ // from .NET documentation:
355
+ // When used with a Single value, the "G9" format specifier ensures that the original Single value
356
+ // successfully round-trips.
357
+ defaultPrecision = 9 ;
358
358
break ;
359
359
case DATATYPE_R8:
360
- defaultPrecision = 15 ;
360
+ // from .NET documentation:
361
+ // When used with a Double value, the "G17" format specifier ensures that the original Double value
362
+ // successfully round-trips.
363
+ defaultPrecision = 17 ;
361
364
break ;
362
365
default :
363
- forcedPrecision = false ;
364
366
break ;
365
367
}
366
368
369
+ int requestedPrecision = precision;
370
+
367
371
if (precision == -1 )
368
372
{
369
373
// no precision specified, use default
370
374
precision = defaultPrecision;
371
-
372
- // set flag to indicate that we're forcing a precision
373
- forcedPrecision = true ;
374
375
}
375
- else
376
+
377
+ int precisionForConversion = precision;
378
+
379
+ if (!isIntegerDataType)
376
380
{
377
- forcedPrecision = false ;
381
+ precisionForConversion += 2 ;
378
382
}
379
383
380
384
if (precision > 0 )
381
385
{
386
+ // compose format string
382
387
char nonIntegerPrecStr[FORMAT_FMTSTR_BUFFER_SIZE];
383
388
if (!isIntegerDataType)
384
389
{
385
- // value of incoming precision would be more than enough
386
- // see diff between printf and ToString precision meaning below
387
- snprintf (nonIntegerPrecStr, FORMAT_FMTSTR_BUFFER_SIZE, " 0.%d" , precision);
390
+ snprintf (nonIntegerPrecStr, FORMAT_FMTSTR_BUFFER_SIZE, " 0.%d" , precisionForConversion);
388
391
}
389
392
390
393
char formatStr[FORMAT_FMTSTR_BUFFER_SIZE];
@@ -400,8 +403,7 @@ int Library_corlib_native_System_Number::Format_G(
400
403
401
404
ret = DoPrintfOnDataType (buffer, formatStr, value);
402
405
403
- // this extra processing is only required for integer types
404
- if (isIntegerDataType && ret > 0 )
406
+ if (ret > 0 )
405
407
{
406
408
// printf and ToString differs on precision numbers:
407
409
// printf("%.05d", 123.4567890) returns "123.45679"
@@ -412,15 +414,34 @@ int Library_corlib_native_System_Number::Format_G(
412
414
bool isNegative = (buffer[0 ] == ' -' );
413
415
int offsetBecauseOfNegativeSign = (isNegative ? 1 : 0 );
414
416
int savedResultLength = ret;
417
+ int exponent = 0 ;
418
+
419
+ // find the exponent character, start with lower case
420
+ char *e = strchr (buffer, ' e' );
421
+
422
+ if (!e)
423
+ {
424
+ // try upper case
425
+ e = strchr (buffer, ' E' );
426
+ }
427
+
428
+ if (e)
429
+ {
430
+ // move past the exponent character
431
+ e++;
432
+
433
+ // convert exponent to digits
434
+ exponent = atoi (e);
435
+ }
415
436
416
437
if (ret > (precision + offsetBecauseOfNegativeSign))
417
438
{
418
439
int dotIndex = GetDotIndex (buffer, ret);
419
440
420
441
int numDigits = 0 ;
421
442
422
- // leave just the required amount of digits, if precision was forced
423
- if (forcedPrecision || precision < defaultPrecision)
443
+ // leave just the required amount of digits
444
+ if (requestedPrecision <= defaultPrecision)
424
445
{
425
446
// find the first digit after the dot
426
447
for (int i = 0 ; i < ret; i++)
@@ -434,12 +455,33 @@ int Library_corlib_native_System_Number::Format_G(
434
455
ret = i + 1 ;
435
456
char first_lost_digit = buffer[ret];
436
457
458
+ // handle various situation, like rounding, exponent, rounding errors
437
459
if (first_lost_digit == ' .' && (ret + 1 ) < savedResultLength)
438
460
{
439
461
first_lost_digit = buffer[ret + 1 ];
462
+ buffer[ret] = 0 ;
463
+ }
464
+ else if (first_lost_digit == ' E' || first_lost_digit == ' e' )
465
+ {
466
+ first_lost_digit = buffer[ret - 1 ];
467
+ buffer[ret] = 0 ;
468
+ ret--;
469
+ }
470
+ else
471
+ {
472
+ buffer[ret] = 0 ;
440
473
}
441
474
442
- buffer[ret] = 0 ;
475
+ if (!isIntegerDataType && buffer[ret - 1 ] == ' 0' )
476
+ {
477
+ // drop last digit in case it's a rounding digit
478
+ memmove (&buffer[ret], &buffer[ret + 1 ], savedResultLength - ret);
479
+
480
+ buffer[ret] = 0 ;
481
+ ret--;
482
+
483
+ break ;
484
+ }
443
485
444
486
if (first_lost_digit >= ' 5' )
445
487
{
@@ -479,11 +521,11 @@ int Library_corlib_native_System_Number::Format_G(
479
521
}
480
522
}
481
523
482
- if ((dotIndex == -1 ) || (dotIndex > (precision + offsetBecauseOfNegativeSign)))
524
+ if ((dotIndex == -1 ) || (dotIndex > (requestedPrecision + offsetBecauseOfNegativeSign)))
483
525
{
484
526
// insert '.', only if request precision requires it
485
- // this is: precision is specified and is more than 1 (taking into account the sign)
486
- if (precision > 0 && (ret - offsetBecauseOfNegativeSign) > 1 )
527
+ // this is: requestedPrecision is specified and is more than 1 (taking into account the sign)
528
+ if (requestedPrecision > 0 && (ret - offsetBecauseOfNegativeSign) > 1 )
487
529
{
488
530
memmove (
489
531
&buffer[2 + offsetBecauseOfNegativeSign],
@@ -494,16 +536,20 @@ int Library_corlib_native_System_Number::Format_G(
494
536
ret++;
495
537
}
496
538
497
- // append 'E+exp'
498
- int exponent = (dotIndex == -1 ) ? savedResultLength - 1 : dotIndex - 1 ;
499
- exponent -= offsetBecauseOfNegativeSign;
539
+ // deal with 'E+exp'
540
+ if (exponent == 0 )
541
+ {
542
+ exponent = (dotIndex == -1 ) ? savedResultLength - 1 : dotIndex - 1 ;
543
+ exponent -= offsetBecauseOfNegativeSign;
544
+ }
545
+
500
546
if (formatChar == ' g' )
501
547
{
502
- ret += snprintf (&buffer[ret], FORMAT_RESULT_BUFFER_SIZE - ret, " e+%02d " , exponent);
548
+ ret += snprintf (&buffer[ret], FORMAT_RESULT_BUFFER_SIZE - ret, " e%+.2d " , exponent);
503
549
}
504
550
else
505
551
{
506
- ret += snprintf (&buffer[ret], FORMAT_RESULT_BUFFER_SIZE - ret, " E+%02d " , exponent);
552
+ ret += snprintf (&buffer[ret], FORMAT_RESULT_BUFFER_SIZE - ret, " E%+.2d " , exponent);
507
553
}
508
554
}
509
555
}
@@ -712,7 +758,7 @@ int Library_corlib_native_System_Number::Format_E(char *buffer, CLR_RT_HeapBlock
712
758
int requestedPrecision = precision;
713
759
714
760
// force extra precision to account for rounding errors
715
- precision = requestedPrecision + 4 ;
761
+ precision = requestedPrecision + 1 ;
716
762
717
763
CLR_DataType dataType = value->DataType ();
718
764
@@ -762,19 +808,7 @@ int Library_corlib_native_System_Number::Format_E(char *buffer, CLR_RT_HeapBlock
762
808
763
809
if (ret > 0 )
764
810
{
765
- int dotIndex = GetDotIndex (buffer, ret);
766
-
767
- // if there is no dot, set the index to the end of the string
768
- if (dotIndex == -1 )
769
- {
770
- dotIndex = ret;
771
- }
772
-
773
- // remove extra precision
774
- memmove (&buffer[dotIndex + requestedPrecision + 1 ], &buffer[dotIndex + precision + 1 ], ret);
775
-
776
- // adjust the string length
777
- ret -= precision + 1 - requestedPrecision;
811
+ int exponent = 0 ;
778
812
779
813
// find the exponent character, start with lower case
780
814
char *e = strchr (buffer, ' e' );
@@ -791,10 +825,66 @@ int Library_corlib_native_System_Number::Format_E(char *buffer, CLR_RT_HeapBlock
791
825
e++;
792
826
793
827
// convert exponent to digits
794
- int expo = atoi (e);
828
+ exponent = atoi (e);
829
+ }
830
+
831
+ int numDigits = 0 ;
832
+ int savedResultLength = ret;
833
+
834
+ int dotIndex = GetDotIndex (buffer, ret);
835
+
836
+ // if there is no dot, set the index to the end of the string
837
+ if (dotIndex == -1 )
838
+ {
839
+ dotIndex = ret;
840
+ }
795
841
796
- // print the exponent with the correct sign and size
797
- snprintf (e, FORMAT_RESULT_BUFFER_SIZE, " %+.3d" , expo);
842
+ // leave just the required amount of digits
843
+ if (requestedPrecision <= precision)
844
+ {
845
+ // find the first digit after the dot
846
+ for (int i = 0 ; i < ret; i++)
847
+ {
848
+ if (buffer[i] >= ' 0' && buffer[i] <= ' 9' )
849
+ {
850
+ numDigits++;
851
+
852
+ if (numDigits == precision)
853
+ {
854
+ ret = i + 1 ;
855
+ char first_lost_digit = buffer[ret];
856
+
857
+ if (first_lost_digit == ' .' && (ret + 1 ) < savedResultLength)
858
+ {
859
+ first_lost_digit = buffer[ret + 1 ];
860
+ }
861
+
862
+ buffer[ret] = 0 ;
863
+
864
+ if (first_lost_digit >= ' 5' )
865
+ {
866
+ int savedRet = ret;
867
+ RoundUpNumStr (buffer, &ret);
868
+
869
+ if (savedRet < ret)
870
+ {
871
+ dotIndex++;
872
+ }
873
+ }
874
+ break ;
875
+ }
876
+ }
877
+ }
878
+ }
879
+
880
+ // now the exponent
881
+ if (formatChar == ' e' )
882
+ {
883
+ snprintf (&buffer[ret], FORMAT_RESULT_BUFFER_SIZE, " e%+.3d" , exponent);
884
+ }
885
+ else
886
+ {
887
+ snprintf (&buffer[ret], FORMAT_RESULT_BUFFER_SIZE, " E%+.3d" , exponent);
798
888
}
799
889
}
800
890
0 commit comments