@@ -424,27 +424,12 @@ php_driver_parse_decimal(char *in, int in_len, mpz_t *number, long *scale TSRMLS
424
424
void
425
425
php_driver_format_integer (mpz_t number , char * * out , int * out_len )
426
426
{
427
- size_t len ;
428
- char * tmp ;
429
-
430
- len = mpz_sizeinbase (number , 10 );
431
- if (mpz_sgn (number ) < 0 )
432
- len ++ ;
433
-
434
- tmp = (char * ) emalloc ((len + 1 ) * sizeof (char ));
435
- mpz_get_str (tmp , 10 , number );
436
-
437
- if (tmp [len - 1 ] == '\0' ) {
438
- len -- ;
439
- } else {
440
- tmp [len ] = '\0' ;
441
- }
442
-
443
- * out = tmp ;
444
- * out_len = len ;
427
+ /* Adding 2 ensures enough space for the null-terminator and negative sign */
428
+ * out = (char * ) emalloc (mpz_sizeinbase (number , 10 ) + 2 );
429
+ mpz_get_str (* out , 10 , number );
430
+ * out_len = strlen (* out );
445
431
}
446
432
447
-
448
433
void
449
434
php_driver_format_decimal (mpz_t number , long scale , char * * out , int * out_len )
450
435
{
@@ -462,91 +447,96 @@ php_driver_format_decimal(mpz_t number, long scale, char **out, int *out_len)
462
447
if (mpz_sgn (number ) < 0 )
463
448
negative = 1 ;
464
449
450
+ // Ultimately, we want to return a string representation of this decimal. So allocate
451
+ // a buffer that could hold this decimal in the worst possible conservative case.
452
+
453
+ // absolute length + negative sign + point sign + scale (in case we end up with a number with leading 0s) +
454
+ // exponent modifier and sign.
455
+ tmp = (char * ) emalloc (len + negative + 1 + scale + 2 );
456
+ mpz_get_str (tmp , 10 , number );
457
+
458
+ // Update len to be the true length of the string representation of |number|. mpz_sizeinbase
459
+ // can return a higher result than the actual length.
460
+ // NOTE: the length of the string includes the negative sign (if present); account for that.
461
+ len = strlen (tmp ) - negative ;
462
+
465
463
point = len - scale ;
466
464
467
- if (scale >= 0 && (point - 1 ) >= -6 ) {
465
+ // We only support numbers with scale >= 0.
466
+ assert (scale >= 0 );
467
+
468
+ if ((point - 1 ) >= -6 ) {
468
469
if (point <= 0 ) {
469
- /* current position */
470
+ // e.g. -0.002 and 0.002
471
+ int shift_start = negative ;
472
+
473
+ // current position
470
474
int i = 0 ;
471
- /* absolute length + negative sign + point sign + leading zeroes */
472
- total = len + negative + 2 + (point * -1 );
473
- tmp = (char * ) emalloc ((total + 1 ) * sizeof (char ));
474
475
476
+ // Move the numeric part (skip leading minus if needed) of tmp right by enough bytes to make room for
477
+ // 0.0000 (as many leading zeroes as necessary).
478
+ memmove (& (tmp [shift_start + 2 - point ]), & (tmp [shift_start ]), len );
479
+
480
+ // This is a (possibly negative) number with a 0 integer part.
475
481
if (negative )
476
482
tmp [i ++ ] = '-' ;
477
483
478
484
tmp [i ++ ] = '0' ;
479
485
tmp [i ++ ] = '.' ;
480
486
487
+ // Add leading zeroes.
481
488
while (point < 0 ) {
482
489
tmp [i ++ ] = '0' ;
483
490
point ++ ;
484
491
}
485
492
486
- mpz_get_str (& (tmp [i ]), 10 , number );
487
-
488
- if (tmp [i + len + negative - 1 ] == '\0' ) {
489
- len -- ;
490
- total -- ;
491
- }
492
-
493
- if (negative )
494
- memmove (& (tmp [i ]), & (tmp [i + 1 ]), len );
495
-
493
+ total = i + len ;
496
494
tmp [total ] = '\0' ;
497
495
} else {
496
+ // e.g. 1.2, -1.2
498
497
/* absolute length + negative sign + point sign */
499
498
total = len + negative + 1 ;
500
- point = point + negative ;
501
- tmp = (char * ) emalloc ((total + 1 ) * sizeof (char ));
502
499
503
- mpz_get_str ( tmp , 10 , number );
500
+ // Insert the decimal point at the right location in the string.
504
501
505
- if (tmp [len + negative - 1 ] == '\0' ) {
506
- len -- ;
507
- total -- ;
508
- }
502
+ // point is the index at which to insert the decimal point, but it assumes we have a positive
503
+ // number. Move it to the right if we have a negative number.
504
+ point += negative ;
509
505
510
506
memmove (& (tmp [point + 1 ]), & (tmp [point ]), total - point );
511
507
512
508
tmp [point ] = '.' ;
513
509
tmp [total ] = '\0' ;
514
510
}
515
511
} else {
512
+ // Very small positive or negative number that we want to express in scientific notation:
513
+ // 0.000000004, -0.000000004
514
+
516
515
int exponent = -1 ;
517
516
int exponent_size = -1 ;
518
- int i = 1 ;
519
517
520
- /* absolute length + negative sign + exponent modifier and sign */
521
- total = len + negative + 2 ;
522
- /* (optional) point sign */
523
- if (len > 1 )
524
- total ++ ;
525
-
526
- /* exponent value */
518
+ // Calculate the exponent value and its size.
527
519
exponent = point - 1 ;
528
520
exponent_size = (int ) ceil (log10 (abs (exponent ) + 2 )) + 1 ;
529
521
530
- total = total + exponent_size ;
531
- tmp = ( char * ) emalloc (( total + 1 ) * sizeof ( char ));
522
+ // If we only have one significant digit, we want to produce a string like
523
+ // 1E-9. If we have more significant digits, then 1.123E-9.
532
524
533
- mpz_get_str (tmp , 10 , number );
525
+ if (len == 1 ) {
526
+ // Simple case; tmp is already leading with our number as we want it. Append E(exp) to it
527
+ // and we're done.
528
+ sprintf (& (tmp [1 + negative ]), "E%+d" , exponent );
529
+ total = 1 + negative + 1 + exponent_size ;
530
+ } else {
531
+ // We have a more complex number. Insert a decimal point after the first digit.
532
+ point = negative ? 2 : 1 ;
533
+ memmove (& (tmp [point + 1 ]), & (tmp [point ]), len - 1 );
534
+ tmp [point ] = '.' ;
534
535
535
- if ( tmp [ len + negative - 1 ] == '\0' ) {
536
- len -- ;
537
- total -- ;
536
+ // Now append the exponent to the end and we're done.
537
+ sprintf ( & ( tmp [ point + len ]), "E%+d" , exponent ) ;
538
+ total = point + len + 1 + exponent_size ;
538
539
}
539
-
540
- if (negative )
541
- i ++ ;
542
-
543
- memmove (& (tmp [i + 1 ]), & (tmp [i ]), len - i );
544
- tmp [i ] = '.' ;
545
- tmp [len + i ++ ] = 'E' ;
546
-
547
- snprintf (& (tmp [len + i ]), exponent_size , "%+d" , exponent );
548
-
549
- tmp [total ] = '\0' ;
550
540
}
551
541
552
542
* out = tmp ;
0 commit comments