Skip to content

Commit b14c02e

Browse files
committed
Replace nanoprintf with es-printf
- Fully replace nanoprintf implementation. - Adjust several System_Number_Format_ handlers.
1 parent 97c7c28 commit b14c02e

File tree

3 files changed

+1427
-1629
lines changed

3 files changed

+1427
-1629
lines changed

src/CLR/CorLib/corlib_native_System_Number.cpp

Lines changed: 136 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ int Library_corlib_native_System_Number::DoPrintfOnDataType(char *buffer, char *
131131

132132
const char *Library_corlib_native_System_Number::GetPrintfLengthModifier(CLR_DataType dataType)
133133
{
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) ? ""
136136
: (dataType == DATATYPE_I4 || dataType == DATATYPE_U4) ? ""
137137
: (dataType == DATATYPE_I8 || dataType == DATATYPE_U8) ? "ll"
138138
: "";
@@ -328,9 +328,6 @@ int Library_corlib_native_System_Number::Format_G(
328328

329329
bool isIntegerDataType = IsIntegerDataType(dataType);
330330

331-
// flag to indicate if we're forcing extra precision for the conversion
332-
bool forcedPrecision = true;
333-
334331
// set default precision for the conversion
335332
int defaultPrecision = 0;
336333
switch (dataType)
@@ -354,37 +351,43 @@ int Library_corlib_native_System_Number::Format_G(
354351
defaultPrecision = 20;
355352
break;
356353
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;
358358
break;
359359
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;
361364
break;
362365
default:
363-
forcedPrecision = false;
364366
break;
365367
}
366368

369+
int requestedPrecision = precision;
370+
367371
if (precision == -1)
368372
{
369373
// no precision specified, use default
370374
precision = defaultPrecision;
371-
372-
// set flag to indicate that we're forcing a precision
373-
forcedPrecision = true;
374375
}
375-
else
376+
377+
int precisionForConversion = precision;
378+
379+
if (!isIntegerDataType)
376380
{
377-
forcedPrecision = false;
381+
precisionForConversion += 2;
378382
}
379383

380384
if (precision > 0)
381385
{
386+
// compose format string
382387
char nonIntegerPrecStr[FORMAT_FMTSTR_BUFFER_SIZE];
383388
if (!isIntegerDataType)
384389
{
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);
388391
}
389392

390393
char formatStr[FORMAT_FMTSTR_BUFFER_SIZE];
@@ -400,8 +403,7 @@ int Library_corlib_native_System_Number::Format_G(
400403

401404
ret = DoPrintfOnDataType(buffer, formatStr, value);
402405

403-
// this extra processing is only required for integer types
404-
if (isIntegerDataType && ret > 0)
406+
if (ret > 0)
405407
{
406408
// printf and ToString differs on precision numbers:
407409
// printf("%.05d", 123.4567890) returns "123.45679"
@@ -412,15 +414,34 @@ int Library_corlib_native_System_Number::Format_G(
412414
bool isNegative = (buffer[0] == '-');
413415
int offsetBecauseOfNegativeSign = (isNegative ? 1 : 0);
414416
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+
}
415436

416437
if (ret > (precision + offsetBecauseOfNegativeSign))
417438
{
418439
int dotIndex = GetDotIndex(buffer, ret);
419440

420441
int numDigits = 0;
421442

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)
424445
{
425446
// find the first digit after the dot
426447
for (int i = 0; i < ret; i++)
@@ -434,12 +455,33 @@ int Library_corlib_native_System_Number::Format_G(
434455
ret = i + 1;
435456
char first_lost_digit = buffer[ret];
436457

458+
// handle various situation, like rounding, exponent, rounding errors
437459
if (first_lost_digit == '.' && (ret + 1) < savedResultLength)
438460
{
439461
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;
440473
}
441474

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+
}
443485

444486
if (first_lost_digit >= '5')
445487
{
@@ -479,11 +521,11 @@ int Library_corlib_native_System_Number::Format_G(
479521
}
480522
}
481523

482-
if ((dotIndex == -1) || (dotIndex > (precision + offsetBecauseOfNegativeSign)))
524+
if ((dotIndex == -1) || (dotIndex > (requestedPrecision + offsetBecauseOfNegativeSign)))
483525
{
484526
// 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)
487529
{
488530
memmove(
489531
&buffer[2 + offsetBecauseOfNegativeSign],
@@ -494,16 +536,20 @@ int Library_corlib_native_System_Number::Format_G(
494536
ret++;
495537
}
496538

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+
500546
if (formatChar == 'g')
501547
{
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);
503549
}
504550
else
505551
{
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);
507553
}
508554
}
509555
}
@@ -712,7 +758,7 @@ int Library_corlib_native_System_Number::Format_E(char *buffer, CLR_RT_HeapBlock
712758
int requestedPrecision = precision;
713759

714760
// force extra precision to account for rounding errors
715-
precision = requestedPrecision + 4;
761+
precision = requestedPrecision + 1;
716762

717763
CLR_DataType dataType = value->DataType();
718764

@@ -762,19 +808,7 @@ int Library_corlib_native_System_Number::Format_E(char *buffer, CLR_RT_HeapBlock
762808

763809
if (ret > 0)
764810
{
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;
778812

779813
// find the exponent character, start with lower case
780814
char *e = strchr(buffer, 'e');
@@ -791,10 +825,66 @@ int Library_corlib_native_System_Number::Format_E(char *buffer, CLR_RT_HeapBlock
791825
e++;
792826

793827
// 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+
}
795841

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);
798888
}
799889
}
800890

0 commit comments

Comments
 (0)