SQLite

Check-in [6dea6f47]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Slightly faster and more precise floating-point to decimal conversion. See forum thread d1387c3979c7f557 for discussion.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 6dea6f4738fc7d003183e94f08bd95181f23e165a966882c0bdc64708a05a607
User & Date: drh 2023-02-24 11:54:40
Context
2023-02-24
13:25
Add the "number_of_cores" TCL command to the testfixture. (check-in: 16ee5a7b user: drh tags: trunk)
12:55
Merge enhancements from trunk into the anytime-config branch. (check-in: 04b2fdf3 user: drh tags: anytime-config)
11:54
Slightly faster and more precise floating-point to decimal conversion. See forum thread d1387c3979c7f557 for discussion. (check-in: 6dea6f47 user: drh tags: trunk)
01:08
Fix an incorrect optimization that was attempted as part of check-in [18de3a8e6b431a07]. (Closed-Leaf check-in: f32055e8 user: drh tags: fp-conversion)
2023-02-23
14:43
Fix harmless compiler warnings and a code indentation error. (check-in: de6c5c6b user: drh tags: trunk)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/printf.c.

138
139
140
141
142
143
144














145
146
147
148
149
150
151
  d = digit;
  digit += '0';
  *val = (*val - d)*10.0;
  return (char)digit;
}
#endif /* SQLITE_OMIT_FLOATING_POINT */















/*
** Set the StrAccum object to an error mode.
*/
void sqlite3StrAccumSetError(StrAccum *p, u8 eError){
  assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG );
  p->accError = eError;
  if( p->mxAlloc ) sqlite3_str_reset(p);







>
>
>
>
>
>
>
>
>
>
>
>
>
>







138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  d = digit;
  digit += '0';
  *val = (*val - d)*10.0;
  return (char)digit;
}
#endif /* SQLITE_OMIT_FLOATING_POINT */

#ifndef SQLITE_OMIT_FLOATING_POINT
/*
** "*val" is a u64.  *msd is a divisor used to extract the
** most significant digit of *val.  Extract that most significant
** digit and return it.
*/
static char et_getdigit_int(u64 *val, u64 *msd){
  u64 x = (*val)/(*msd);
  *val -= x*(*msd);
  if( *msd>=10 ) *msd /= 10;
  return '0' + (char)(x & 15);
}
#endif /* SQLITE_OMIT_FLOATING_POINT */

/*
** Set the StrAccum object to an error mode.
*/
void sqlite3StrAccumSetError(StrAccum *p, u8 eError){
  assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG );
  p->accError = eError;
  if( p->mxAlloc ) sqlite3_str_reset(p);
230
231
232
233
234
235
236


237
238
239
240
241
242
243
  etByte done;               /* Loop termination flag */
  etByte cThousand;          /* Thousands separator for %d and %u */
  etByte xtype = etINVALID;  /* Conversion paradigm */
  u8 bArgList;               /* True for SQLITE_PRINTF_SQLFUNC */
  char prefix;               /* Prefix character.  "+" or "-" or " " or '\0'. */
  sqlite_uint64 longvalue;   /* Value for integer types */
  LONGDOUBLE_TYPE realvalue; /* Value for real types */


  const et_info *infop;      /* Pointer to the appropriate info structure */
  char *zOut;                /* Rendering buffer */
  int nOut;                  /* Size of the rendering buffer */
  char *zExtra = 0;          /* Malloced memory used by some conversion */
#ifndef SQLITE_OMIT_FLOATING_POINT
  int  exp, e2;              /* exponent of real numbers */
  int nsd;                   /* Number of significant digits returned */







>
>







244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
  etByte done;               /* Loop termination flag */
  etByte cThousand;          /* Thousands separator for %d and %u */
  etByte xtype = etINVALID;  /* Conversion paradigm */
  u8 bArgList;               /* True for SQLITE_PRINTF_SQLFUNC */
  char prefix;               /* Prefix character.  "+" or "-" or " " or '\0'. */
  sqlite_uint64 longvalue;   /* Value for integer types */
  LONGDOUBLE_TYPE realvalue; /* Value for real types */
  sqlite_uint64 msd;         /* Divisor to get most-significant-digit
                             ** of longvalue */
  const et_info *infop;      /* Pointer to the appropriate info structure */
  char *zOut;                /* Rendering buffer */
  int nOut;                  /* Size of the rendering buffer */
  char *zExtra = 0;          /* Malloced memory used by some conversion */
#ifndef SQLITE_OMIT_FLOATING_POINT
  int  exp, e2;              /* exponent of real numbers */
  int nsd;                   /* Number of significant digits returned */
536
537
538
539
540
541
542

543
544














545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563


564
565
566
567
568
569
570
571
572
573
574
575
576
577
578



579


580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604


605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622




623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638

639




640

641
642
643
644
645
646
647
#endif
        if( realvalue<0.0 ){
          realvalue = -realvalue;
          prefix = '-';
        }else{
          prefix = flag_prefix;
        }

        if( xtype==etGENERIC && precision>0 ) precision--;
        testcase( precision>0xfff );














        idx = precision & 0xfff;
        rounder = arRound[idx%10];
        while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; }
        if( xtype==etFLOAT ){
          double rx = (double)realvalue;
          sqlite3_uint64 u;
          int ex;
          memcpy(&u, &rx, sizeof(u));
          ex = -1023 + (int)((u>>52)&0x7ff);
          if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16;
          realvalue += rounder;
        }
        /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
        exp = 0;
        if( sqlite3IsNaN((double)realvalue) ){
          bufpt = "NaN";
          length = 3;
          break;
        }


        if( realvalue>0.0 ){
          LONGDOUBLE_TYPE scale = 1.0;
          while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;}
          while( realvalue>=1e10*scale && exp<=350 ){ scale *= 1e10; exp+=10; }
          while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; }
          realvalue /= scale;
          while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
          while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
          if( exp>350 ){
            bufpt = buf;
            buf[0] = prefix;
            memcpy(buf+(prefix!=0),"Inf",4);
            length = 3+(prefix!=0);
            break;
          }



        }


        bufpt = buf;
        /*
        ** If the field type is etGENERIC, then convert to either etEXP
        ** or etFLOAT, as appropriate.
        */
        if( xtype!=etFLOAT ){
          realvalue += rounder;
          if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
        }
        if( xtype==etGENERIC ){
          flag_rtz = !flag_alternateform;
          if( exp<-4 || exp>precision ){
            xtype = etEXP;
          }else{
            precision = precision - exp;
            xtype = etFLOAT;
          }
        }else{
          flag_rtz = flag_altform2;
        }
        if( xtype==etEXP ){
          e2 = 0;
        }else{
          e2 = exp;
        }


        {
          i64 szBufNeeded;           /* Size of a temporary buffer needed */
          szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15;
          if( szBufNeeded > etBUFSIZE ){
            bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded);
            if( bufpt==0 ) return;
          }
        }
        zOut = bufpt;
        nsd = 16 + flag_altform2*10;
        flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2;
        /* The sign in front of the number */
        if( prefix ){
          *(bufpt++) = prefix;
        }
        /* Digits prior to the decimal point */
        if( e2<0 ){
          *(bufpt++) = '0';




        }else{
          for(; e2>=0; e2--){
            *(bufpt++) = et_getdigit(&realvalue,&nsd);
          }
        }
        /* The decimal point */
        if( flag_dp ){
          *(bufpt++) = '.';
        }
        /* "0" digits after the decimal point but before the first
        ** significant digit of the number */
        for(e2++; e2<0; precision--, e2++){
          assert( precision>0 );
          *(bufpt++) = '0';
        }
        /* Significant digits after the decimal point */

        while( (precision--)>0 ){




          *(bufpt++) = et_getdigit(&realvalue,&nsd);

        }
        /* Remove trailing zeros and the "." if no digits follow the "." */
        if( flag_rtz && flag_dp ){
          while( bufpt[-1]=='0' ) *(--bufpt) = 0;
          assert( bufpt>zOut );
          if( bufpt[-1]=='.' ){
            if( flag_altform2 ){







>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
|
|
|
|
|
|
|
<
<
|
|
|
|
|
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
|
>
>
|




<
<
<
<
















>
>









<








>
>
>
>
















>
|
>
>
>
>
|
>







552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587


588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620




621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647

648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
#endif
        if( realvalue<0.0 ){
          realvalue = -realvalue;
          prefix = '-';
        }else{
          prefix = flag_prefix;
        }
        exp = 0;
        if( xtype==etGENERIC && precision>0 ) precision--;
        testcase( precision>0xfff );
        if( realvalue<1.0e+16
         && realvalue==(LONGDOUBLE_TYPE)(longvalue = (u64)realvalue)
        ){
          /* Number is a pure integer that can be represented as u64 */
          for(msd=1; msd*10<=longvalue; msd *= 10, exp++){}
          if( exp>precision && xtype!=etFLOAT ){
            u64 rnd = msd/2;
            int kk = precision;
            while( kk-- > 0 ){  rnd /= 10; }
            longvalue += rnd;
          }
        }else{
          msd = 0;
          longvalue = 0;  /* To prevent a compiler warning */
          idx = precision & 0xfff;
          rounder = arRound[idx%10];
          while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; }
          if( xtype==etFLOAT ){
            double rx = (double)realvalue;
            sqlite3_uint64 u;
            int ex;
            memcpy(&u, &rx, sizeof(u));
            ex = -1023 + (int)((u>>52)&0x7ff);
            if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16;
            realvalue += rounder;
          }


          if( sqlite3IsNaN((double)realvalue) ){
            bufpt = "NaN";
            length = 3;
            break;
          }

          /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
          if( ALWAYS(realvalue>0.0) ){
            LONGDOUBLE_TYPE scale = 1.0;
            while( realvalue>=1e100*scale && exp<=350){ scale*=1e100;exp+=100;}
            while( realvalue>=1e10*scale && exp<=350 ){ scale*=1e10; exp+=10; }
            while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; }
            realvalue /= scale;
            while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
            while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
            if( exp>350 ){
              bufpt = buf;
              buf[0] = prefix;
              memcpy(buf+(prefix!=0),"Inf",4);
              length = 3+(prefix!=0);
              break;
            }
            if( xtype!=etFLOAT ){
              realvalue += rounder;
              if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
            }
          }
        }

        /*
        ** If the field type is etGENERIC, then convert to either etEXP
        ** or etFLOAT, as appropriate.
        */




        if( xtype==etGENERIC ){
          flag_rtz = !flag_alternateform;
          if( exp<-4 || exp>precision ){
            xtype = etEXP;
          }else{
            precision = precision - exp;
            xtype = etFLOAT;
          }
        }else{
          flag_rtz = flag_altform2;
        }
        if( xtype==etEXP ){
          e2 = 0;
        }else{
          e2 = exp;
        }
        nsd = 16 + flag_altform2*10;
        bufpt = buf;
        {
          i64 szBufNeeded;           /* Size of a temporary buffer needed */
          szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15;
          if( szBufNeeded > etBUFSIZE ){
            bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded);
            if( bufpt==0 ) return;
          }
        }
        zOut = bufpt;

        flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2;
        /* The sign in front of the number */
        if( prefix ){
          *(bufpt++) = prefix;
        }
        /* Digits prior to the decimal point */
        if( e2<0 ){
          *(bufpt++) = '0';
        }else if( msd>0 ){
          for(; e2>=0; e2--){
            *(bufpt++) = et_getdigit_int(&longvalue,&msd);
          }
        }else{
          for(; e2>=0; e2--){
            *(bufpt++) = et_getdigit(&realvalue,&nsd);
          }
        }
        /* The decimal point */
        if( flag_dp ){
          *(bufpt++) = '.';
        }
        /* "0" digits after the decimal point but before the first
        ** significant digit of the number */
        for(e2++; e2<0; precision--, e2++){
          assert( precision>0 );
          *(bufpt++) = '0';
        }
        /* Significant digits after the decimal point */
        if( msd>0 ){
          while( (precision--)>0 ){
            *(bufpt++) = et_getdigit_int(&longvalue,&msd);
          }
        }else{
          while( (precision--)>0 ){
            *(bufpt++) = et_getdigit(&realvalue,&nsd);
          }
        }
        /* Remove trailing zeros and the "." if no digits follow the "." */
        if( flag_rtz && flag_dp ){
          while( bufpt[-1]=='0' ) *(--bufpt) = 0;
          assert( bufpt>zOut );
          if( bufpt[-1]=='.' ){
            if( flag_altform2 ){

Changes to test/printf.test.

3781
3782
3783
3784
3785
3786
3787






































3788
3789

# 2020-05-23
# ticket 23439ea582241138
#
do_execsql_test printf-16.1 {
  SELECT printf('%.*g',2147483647,0.01);
} {0.01}







































finish_test







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827

# 2020-05-23
# ticket 23439ea582241138
#
do_execsql_test printf-16.1 {
  SELECT printf('%.*g',2147483647,0.01);
} {0.01}

# 2023-02-23 https://sqlite.org/forum/forumpost/d1387c3979c7f557
# Loss of precision when doing floating-point to decimal
# conversions on values that have no factional part.
#
do_execsql_test printf-17.1 {
  SELECT format('%!.20g', 13.0);
} 13.0
do_execsql_test printf-17.2 {
  SELECT format('%.3e', 199990000.0);
} 2.000e+08
do_execsql_test printf-17.3 {
  SELECT format('%.3f', 199990000.0);
} 199990000.000
do_execsql_test printf-17.4 {
  SELECT format('%.3g', 199990000.0);
} 2e+08
do_execsql_test printf-17.5 {
  SELECT format('%.4e', 199990000.0);
} 1.9999e+08
do_execsql_test printf-17.6 {
  SELECT format('%.4f', 199990000.0);
} 199990000.0000
do_execsql_test printf-17.7 {
  SELECT format('%.4g', 199990000.0);
} 2e+08
do_execsql_test printf-17.8 {
  SELECT format('%.5e', 199990000.0);
} 1.99990e+08
do_execsql_test printf-17.9 {
  SELECT format('%.5f', 199990000.0);
} 199990000.00000
do_execsql_test printf-17.10 {
  SELECT format('%.5g', 199990000.0);
} 1.9999e+08
do_execsql_test printf-17.11 {
  SELECT format('%.30f',1.0000000000000000076e-50);
} 0.000000000000000000000000000000

finish_test