SQLite4
Check-in [8ede88c1df]
Not logged in

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

Overview
Comment:Fold in Peter Reid's fixes and enhancements to the sqlite4_num object.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8ede88c1df96790b39ab47b79bc83b7cbbb21898
User & Date: drh 2013-02-14 15:32:53
Context
2013-02-15
19:20
Fix a crash bug found by TH4. check-in: 6453240a0f user: drh tags: trunk
2013-02-14
15:32
Fold in Peter Reid's fixes and enhancements to the sqlite4_num object. check-in: 8ede88c1df user: drh tags: trunk
01:51
Fix sqlite4_num_to_text when formatting an integer with a negative exponent. It was leaving an uninitialized byte in the destination string. Leaf check-in: 3cac6cdb86 user: peterreid tags: num_work
2013-02-13
12:28
Change the API for binding strings and blobs so that the destructor now carries an extra argument which can be used as the sqlite4_env pointer, thus allowing functions like sqlite4_free() to be used as a string or blob destructor. check-in: 56335097b1 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/math.c.

   211    211     r.sign = A.sign ^ B.sign;
   212    212     r.approx = A.approx | B.approx;
   213    213     if( r.approx==0 && A.m%B.m!=0 ) r.approx = 1;
   214    214     r.m = A.m/B.m;
   215    215     r.e = A.e - B.e;
   216    216     return r;
   217    217   }
          218  +
          219  +/*
          220  +** Test if A is infinite.
          221  +*/
          222  +int sqlite4_num_isinf(sqlite4_num A){
          223  +  return A.e>SQLITE4_MX_EXP && A.m!=0;
          224  +}
          225  +
          226  +/*
          227  +** Test if A is NaN.
          228  +*/
          229  +int sqlite4_num_isnan(sqlite4_num A){
          230  +  return A.e>SQLITE4_MX_EXP && A.m==0; 
          231  +}
   218    232   
   219    233   /*
   220    234   ** Compare numbers A and B.  Return:
   221    235   **
   222    236   **    1     if A<B
   223    237   **    2     if A==B
   224    238   **    3     if A>B
................................................................................
   237    251       return A.sign ? 1 : 3;
   238    252     }
   239    253     if( B.e>SQLITE4_MX_EXP ){
   240    254       if( B.m==0 ) return 0;
   241    255       return B.sign ? 3 : 1;
   242    256     }
   243    257     if( A.sign!=B.sign ){
          258  +    if ( A.m==0 && B.m==0 ) return 2;
   244    259       return A.sign ? 1 : 3;
   245    260     }
   246    261     adjustExponent(&A, &B);
   247    262     if( A.sign ){
   248    263       sqlite4_num t = A;
   249    264       A = B;
   250    265       B = t;
................................................................................
   319    334       i = incr;
   320    335     }else if( zIn[0]=='+' ){
   321    336       i = incr;
   322    337     }else{
   323    338       i = 0;
   324    339     }
   325    340     if( nIn<=0 ) goto not_a_valid_number;
   326         -  if( nIn>=incr*2
          341  +  if( nIn>=incr*3
   327    342      && ((c=zIn[i])=='i' || c=='I')
   328    343      && ((c=zIn[i+incr])=='n' || c=='N')
   329    344      && ((c=zIn[i+incr*2])=='f' || c=='F')
   330    345     ){
   331    346       r.e = SQLITE4_MX_EXP+1;
   332    347       r.m = nIn<=i+incr*3 || zIn[i+incr*3]==0;
   333    348       return r;
   334    349     }
   335    350     while( i<nIn && (c = zIn[i])!=0 ){
   336    351       i += incr;
   337    352       if( c>='0' && c<='9' ){
   338         -      if( c==0 && nDigit==0 ){
          353  +      if( c=='0' && nDigit==0 ){
   339    354           if( seenRadix && r.e > -(SQLITE4_MX_EXP+1000) ) r.e--;
   340    355           continue;
   341    356         }
   342    357         nDigit++;
   343    358         if( nDigit<=18 ){
   344    359           r.m = (r.m*10) + c - '0';
   345    360           if( seenRadix ) r.e--;
   346    361         }else{
   347    362           if( c!='0' ) r.approx = 1;
   348    363           if( !seenRadix ) r.e++;
   349    364         }
   350    365       }else if( c=='.' ){
   351    366         seenRadix = 1;
   352         -    }else{
          367  +    }else if( c=='e' || c=='E' ){
          368  +      int exp = 0;
          369  +      int expsign = 0;
          370  +      int nEDigit = 0;
          371  +      if( zIn[i]=='-' ){
          372  +        expsign = 1;
          373  +        i += incr;
          374  +      }else if( zIn[i]=='+' ){
          375  +        i += incr;
          376  +      }
          377  +      if( i>=nIn ) goto not_a_valid_number;
          378  +      while( i<nIn && (c = zIn[i])!=0 ){
          379  +        i += incr;
          380  +        if( c<'0' || c>'9' ) goto not_a_valid_number;
          381  +        if( c=='0' && nEDigit==0 ) continue;
          382  +        nEDigit++;
          383  +        if( nEDigit>3 ) goto not_a_valid_number;
          384  +        exp = exp*10 + c - '0';
          385  +      }
          386  +      if( expsign ) exp = -exp;
          387  +      r.e += exp;
   353    388         break;
          389  +    }else{
          390  +      goto not_a_valid_number;
   354    391       }
   355    392     }
   356         -  if( c=='e' || c=='E' ){
   357         -    int exp = 0;
   358         -    int expsign = 0;
   359         -    int nEDigit = 0;
   360         -    if( zIn[i]=='-' ){
   361         -      expsign = 1;
   362         -      i += incr;
   363         -    }else if( zIn[i]=='+' ){
   364         -      i += incr;
   365         -    }
   366         -    if( i>=nIn ) goto not_a_valid_number;
   367         -    while( i<nIn && (c = zIn[i])!=0  ){
   368         -      i += incr;
   369         -      if( c<'0' || c>'9' ) break;
   370         -      if( c=='0' && nEDigit==0 ) continue;
   371         -      nEDigit++;
   372         -      if( nEDigit>3 ) goto not_a_valid_number;
   373         -      exp = exp*10 + c - '0';
   374         -    }
   375         -    if( expsign ) exp = -exp;
   376         -    r.e += exp;
   377         -  }
   378         -  if( c!=0 ) goto not_a_valid_number;
   379    393     return r;
   380    394     
   381    395   not_a_valid_number:
   382    396     r.e = SQLITE4_MX_EXP+1;
   383    397     r.m = 0;
   384    398     return r;  
   385    399   }
          400  +
          401  +/*
          402  +** Convert an sqlite4_int64 to a number and return that number.
          403  +*/
          404  +sqlite4_num sqlite4_num_from_int64(sqlite4_int64 n){
          405  +  sqlite4_num r;
          406  +  r.approx = 0;
          407  +  r.e = 0;
          408  +  r.sign = n < 0;
          409  +  if( n>=0 ){
          410  +    r.m = n;
          411  +  }else if( n!=SMALLEST_INT64 ){
          412  +    r.m = -n;
          413  +  }else{
          414  +    r.m = 1+(u64)LARGEST_INT64;
          415  +  }
          416  +  return r;
          417  +}
   386    418   
   387    419   /*
   388    420   ** Convert an integer into text in the buffer supplied. The
   389    421   ** text is zero-terminated and right-justified in the buffer.
   390    422   ** A pointer to the first character of text is returned.
   391    423   **
   392    424   ** The buffer needs to be at least 21 bytes in length.
................................................................................
   465    497       zNum += m;
   466    498       n -= m;
   467    499       removeTrailingZeros(zNum, &n);
   468    500       if( n>0 ){
   469    501         zOut[0] = '.';
   470    502         memcpy(zOut+1, zNum, n);
   471    503         nOut += n;
          504  +      zOut[n+1] = 0;
          505  +    }else{
          506  +      zOut[0] = 0;
   472    507       }
   473         -    zOut[n+1] = 0;
   474    508       return nOut;
   475    509     }
   476    510     if( x.e<0 && x.e >= -n-5 ){
   477    511       /* Values less than 1 and with no more than 5 subsequent zeros prior
   478    512       ** to the first significant digit.  Ex:  0.0000012345 */
   479    513       int j = -(n + x.e);
   480    514       memcpy(zOut, "0.", 2);

Changes to src/sqlite.h.in.

  4352   4352   ** Every number in SQLite is represented in memory by an instance of
  4353   4353   ** the following object.
  4354   4354   */
  4355   4355   typedef struct sqlite4_num sqlite4_num;
  4356   4356   struct sqlite4_num {
  4357   4357     unsigned char sign;     /* Sign of the overall value */
  4358   4358     unsigned char approx;   /* True if the value is approximate */
  4359         -  unsigned short e;       /* The exponent. */
         4359  +  short e;                /* The exponent. */
  4360   4360     sqlite4_uint64 m;       /* The significant */
  4361   4361   };
  4362   4362   
  4363   4363   /*
  4364   4364   ** CAPI4REF: Operations On SQLite Number Objects
  4365   4365   */
  4366   4366   sqlite4_num sqlite4_num_add(sqlite4_num, sqlite4_num);

Changes to src/sqliteInt.h.

   460    460   /*
   461    461   ** Constants for the largest and smallest possible 64-bit signed integers.
   462    462   ** These macros are designed to work correctly on both 32-bit and 64-bit
   463    463   ** compilers.
   464    464   */
   465    465   #define LARGEST_INT64  (0xffffffff|(((i64)0x7fffffff)<<32))
   466    466   #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
   467         -#define LARGEST_UINT64  (0xffffffff|(((i64)0xffffffff)<<32))
          467  +#define LARGEST_UINT64  (0xffffffff|(((u64)0xffffffff)<<32))
   468    468   
   469    469   /* 
   470    470   ** Round up a number to the next larger multiple of 8.  This is used
   471    471   ** to force 8-byte alignment on 64-bit architectures.
   472    472   */
   473    473   #define ROUND8(x)     (((x)+7)&~7)
   474    474   

Added test/num.test.

            1  +# 2001 September 15
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library.  The
           12  +# focus of this file is testing the sqlite_*_printf() interface.
           13  +#
           14  +# $Id: printf.test,v 1.31 2009/02/01 00:21:10 drh Exp $
           15  +
           16  +set testdir [file dirname $argv0]
           17  +source $testdir/tester.tcl
           18  +
           19  +do_test num-1.1.1 {
           20  +  sqlite4_num_compare 20 20 
           21  +} {equal}
           22  +do_test num-1.1.2 {
           23  +  sqlite4_num_compare 20 2e1
           24  +} {equal}
           25  +do_test num-1.1.3 {
           26  +  sqlite4_num_compare -00034 -3.4e1
           27  +} {equal}
           28  +do_test num-1.1.4 {
           29  +  sqlite4_num_compare -inf +inf
           30  +} {lesser}
           31  +do_test num-1.1.5 {
           32  +  sqlite4_num_compare -inf 0
           33  +} {lesser}
           34  +do_test num-1.1.6 {
           35  +  sqlite4_num_compare inf 4
           36  +} {greater}
           37  +do_test num-1.1.7 {
           38  +  sqlite4_num_compare nan 7
           39  +} {incomparable}
           40  +# Is +0 > -0?
           41  +#do_test num-equal-1.1.4 {
           42  +#  sqlite4_num_compare +0 -0
           43  +#} {equal}
           44  +
           45  +do_test num-2.1.1 {
           46  +  sqlite4_num_to_text [sqlite4_num_from_text 37]
           47  +} {37}
           48  +do_test num-2.1.2 {
           49  +  sqlite4_num_to_text [sqlite4_num_from_text 37 2]
           50  +} {37}
           51  +do_test num-2.1.4 {
           52  +  sqlite4_num_compare [sqlite4_num_from_text 2.9e2X 5] 290
           53  +} {equal}
           54  +do_test num-2.1.5 {
           55  +  sqlite4_num_isnan [sqlite4_num_from_text inf 2]
           56  +} {true}
           57  +do_test num-2.1.6 {
           58  +  sqlite4_num_isinf [sqlite4_num_from_text inf 3]
           59  +} {true}
           60  +
           61  +do_test num-3.1.1 {
           62  +  sqlite4_num_to_text [sqlite4_num_add 5 7]
           63  +} {12}
           64  +
           65  +do_test num-4.1.1 {
           66  +  sqlite4_num_to_text [sqlite4_num_sub 9 3]
           67  +} {6}
           68  +do_test num-4.1.2 {
           69  +  sqlite4_num_to_text [sqlite4_num_sub 5 12]
           70  +} {-7}
           71  +do_test num-4.2.1 {
           72  +  sqlite4_num_compare [sqlite4_num_sub 1 1] [sqlite4_num_sub -1 -1]
           73  +} {equal}
           74  +
           75  +do_test num-5.1.1 {
           76  +  sqlite4_num_to_text [sqlite4_num_mul 9 8]
           77  +} {72}
           78  +
           79  +do_test num-6.1.1 {
           80  +  sqlite4_num_to_text [sqlite4_num_div 6 5]
           81  +} {1.2}
           82  +do_test num-6.1.2 {
           83  +  sqlite4_num_compare 2 [sqlite4_num_div 2 1]
           84  +} {equal}
           85  +do_test num-6.1.3 {
           86  +  sqlite4_num_to_text [sqlite4_num_div 2 1]
           87  +} {2}
           88  +do_test num-6.1.4 {
           89  +  sqlite4_num_to_text [sqlite4_num_div 22 10]
           90  +} {2.2}
           91  +finish_test

Changes to test/test_main.c.

  4348   4348         Tcl_AppendResult(interp, " ", aOpt[i].zOptName);
  4349   4349       }
  4350   4350       return TCL_ERROR;
  4351   4351     }
  4352   4352     sqlite4_test_control(SQLITE4_TESTCTRL_OPTIMIZATIONS, db, mask);
  4353   4353     return TCL_OK;
  4354   4354   }
         4355  +
         4356  +#define NUM_FORMAT "sign:%d approx:%d e:%d m:%lld"
         4357  +
         4358  +/* Append a return value representing a sqlite4_num.
         4359  +*/
         4360  +static void append_num_result( Tcl_Interp *interp, sqlite4_num A ){
         4361  +  char buf[100];
         4362  +  sprintf( buf, NUM_FORMAT, A.sign, A.approx, A.e, A.m );
         4363  +  Tcl_AppendResult(interp, buf, 0);
         4364  +}
         4365  +
         4366  +/* Convert a string either representing a sqlite4_num (listing its fields as
         4367  +** returned by append_num_result) or that can be parsed as one. Invalid
         4368  +** strings become NaN.
         4369  +*/
         4370  +static sqlite4_num test_parse_num( char *arg ){
         4371  +  sqlite4_num A;
         4372  +  int sign, approx, e;
         4373  +  if( sscanf( arg, NUM_FORMAT, &sign, &approx, &e, &A.m)==4 ){
         4374  +    A.sign = sign;
         4375  +    A.approx = approx;
         4376  +    A.e = e;
         4377  +    return A;
         4378  +  } else {
         4379  +    return sqlite4_num_from_text(arg, -1, 0);
         4380  +  }
         4381  +}
         4382  +
         4383  +/* Convert return values of sqlite4_num to strings that will be readable in
         4384  +** the tests.
         4385  +*/
         4386  +static char *describe_num_comparison( int code ){
         4387  +  switch( code ){
         4388  +    case 0: return "incomparable";
         4389  +    case 1: return "lesser";
         4390  +    case 2: return "equal";
         4391  +    case 3: return "greater";
         4392  +    default: return "error"; 
         4393  +  }
         4394  +}
         4395  +
         4396  +/* Compare two numbers A and B. Returns "incomparable", "lesser", "equal",
         4397  +** "greater", or "error".
         4398  +*/
         4399  +static int test_num_compare(
         4400  +  void *NotUsed,
         4401  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         4402  +  int argc,              /* Number of arguments */
         4403  +  char **argv            /* Text of each argument */
         4404  +){
         4405  +  sqlite4_num A, B;
         4406  +  int cmp;
         4407  +  if( argc!=3 ){
         4408  +    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
         4409  +       " NUM NUM\"", 0);
         4410  +    return TCL_ERROR;
         4411  +  }
         4412  +  
         4413  +  A = test_parse_num( argv[1] );
         4414  +  B = test_parse_num( argv[2] );
         4415  +  cmp = sqlite4_num_compare(A, B);
         4416  +  Tcl_AppendResult( interp, describe_num_comparison( cmp ), 0);
         4417  +  return TCL_OK; 
         4418  +}
         4419  +
         4420  +/* Create a sqlite4_num from a string. The optional second argument specifies
         4421  +** how many bytes may be read.
         4422  +*/
         4423  +static int test_num_from_text(
         4424  +  void *NotUsed,
         4425  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         4426  +  int argc,              /* Number of arguments */
         4427  +  char **argv            /* Text of each argument */
         4428  +){
         4429  +  sqlite4_num A;
         4430  +  int len;
         4431  +  if( argc!=2 && argc!=3 ){
         4432  +    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
         4433  +      " STRING\" or \"", argv[0], " STRING INTEGER\"", 0);
         4434  +    return TCL_ERROR;
         4435  +  }
         4436  +
         4437  +  if( argc==3 ){
         4438  +    if ( Tcl_GetInt(interp, argv[2], &len) ) return TCL_ERROR; 
         4439  +  }else{
         4440  +    len = -1;
         4441  +  }
         4442  +
         4443  +  A = sqlite4_num_from_text( argv[1], len, 0 );
         4444  +  append_num_result(interp, A);
         4445  +  return TCL_OK;
         4446  +}
         4447  +
         4448  +static int test_num_to_text(
         4449  +  void *NotUsed,
         4450  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         4451  +  int argc,              /* Number of arguments */
         4452  +  char **argv            /* Text of each argument */
         4453  +){
         4454  +  char text[30];
         4455  +  if( argc!=2 ){
         4456  +    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
         4457  +      " NUM\"", 0);
         4458  +    return TCL_ERROR;
         4459  +  }
         4460  +  sqlite4_num_to_text( test_parse_num( argv[1] ), text );
         4461  +  Tcl_AppendResult( interp, text, 0 );
         4462  +  return TCL_OK;
         4463  +}
         4464  +
         4465  +static int test_num_binary_op(
         4466  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         4467  +  int argc,              /* Number of arguments */
         4468  +  char **argv,            /* Text of each argument */
         4469  +  sqlite4_num (*op) (sqlite4_num, sqlite4_num)
         4470  +){
         4471  +  sqlite4_num A, B, R;
         4472  +  if( argc!=3 ){
         4473  +    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
         4474  +      " NUM NUM\"", 0);
         4475  +    return TCL_ERROR;
         4476  +  }
         4477  +  A = test_parse_num(argv[1]);
         4478  +  B = test_parse_num(argv[2]);
         4479  +  R = op(A, B);
         4480  +  append_num_result(interp, R);
         4481  +  return TCL_OK;
         4482  +}
         4483  +
         4484  +static int test_num_add(
         4485  +  void *NotUsed,
         4486  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         4487  +  int argc,              /* Number of arguments */
         4488  +  char **argv            /* Text of each argument */
         4489  +){
         4490  +  return test_num_binary_op( interp, argc, argv, sqlite4_num_add );
         4491  +}
         4492  +
         4493  +static int test_num_sub(
         4494  +  void *NotUsed,
         4495  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         4496  +  int argc,              /* Number of arguments */
         4497  +  char **argv            /* Text of each argument */
         4498  +){
         4499  +  return test_num_binary_op( interp, argc, argv, sqlite4_num_sub );
         4500  +}
         4501  +
         4502  +static int test_num_mul(
         4503  +  void *NotUsed,
         4504  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         4505  +  int argc,              /* Number of arguments */
         4506  +  char **argv            /* Text of each argument */
         4507  +){
         4508  +  return test_num_binary_op( interp, argc, argv, sqlite4_num_mul );
         4509  +}
         4510  +
         4511  +static int test_num_div(
         4512  +  void *NotUsed,
         4513  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         4514  +  int argc,              /* Number of arguments */
         4515  +  char **argv            /* Text of each argument */
         4516  +){
         4517  +  return test_num_binary_op( interp, argc, argv, sqlite4_num_div );
         4518  +}
         4519  +
         4520  +static int test_num_predicate(
         4521  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         4522  +  int argc,              /* Number of arguments */
         4523  +  char **argv,            /* Text of each argument */
         4524  +  int (*pred) (sqlite4_num)
         4525  +){
         4526  +  sqlite4_num A;
         4527  +  if( argc!=2 ){
         4528  +    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
         4529  +      " NUM\"", 0);
         4530  +    return TCL_ERROR;
         4531  +  }
         4532  +  A = test_parse_num(argv[1]);
         4533  +  Tcl_AppendResult(interp, pred(A) ? "true" : "false", 0);  
         4534  +  return TCL_OK;
         4535  +}
         4536  +
         4537  +static int test_num_isinf(
         4538  +  void *NotUsed,
         4539  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         4540  +  int argc,              /* Number of arguments */
         4541  +  char **argv            /* Text of each argument */
         4542  +){
         4543  +  return test_num_predicate( interp, argc, argv, sqlite4_num_isinf );
         4544  +}
         4545  +
         4546  +static int test_num_isnan(
         4547  +  void *NotUsed,
         4548  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         4549  +  int argc,              /* Number of arguments */
         4550  +  char **argv            /* Text of each argument */
         4551  +){
         4552  +  return test_num_predicate( interp, argc, argv, sqlite4_num_isnan );
         4553  +}
  4355   4554   
  4356   4555   /*
  4357   4556   ** Register commands with the TCL interpreter.
  4358   4557   */
  4359   4558   int Sqlitetest1_Init(Tcl_Interp *interp){
  4360   4559     extern int sqlite4_search_count;
  4361   4560     extern int sqlite4_found_count;
................................................................................
  4401   4600        { "sqlite_set_magic",              (Tcl_CmdProc*)sqlite_set_magic      },
  4402   4601        { "sqlite4_interrupt",             (Tcl_CmdProc*)test_interrupt        },
  4403   4602        { "sqlite_delete_function",        (Tcl_CmdProc*)delete_function       },
  4404   4603        { "sqlite_delete_collation",       (Tcl_CmdProc*)delete_collation      },
  4405   4604        { "sqlite4_get_autocommit",        (Tcl_CmdProc*)get_autocommit        },
  4406   4605        { "sqlite4_stack_used",            (Tcl_CmdProc*)test_stack_used       },
  4407   4606        { "printf",                        (Tcl_CmdProc*)test_printf           },
  4408         -     { "sqlite4IoTrace",              (Tcl_CmdProc*)test_io_trace         },
         4607  +     { "sqlite4IoTrace",                (Tcl_CmdProc*)test_io_trace         },
         4608  +     { "sqlite4_num_compare",           (Tcl_CmdProc*)test_num_compare      }, 
         4609  +     { "sqlite4_num_from_text",         (Tcl_CmdProc*)test_num_from_text    }, 
         4610  +     { "sqlite4_num_to_text",           (Tcl_CmdProc*)test_num_to_text      },
         4611  +     { "sqlite4_num_add",               (Tcl_CmdProc*)test_num_add          },
         4612  +     { "sqlite4_num_sub",               (Tcl_CmdProc*)test_num_sub          },
         4613  +     { "sqlite4_num_mul",               (Tcl_CmdProc*)test_num_mul          },
         4614  +     { "sqlite4_num_div",               (Tcl_CmdProc*)test_num_div          },
         4615  +     { "sqlite4_num_isinf",             (Tcl_CmdProc*)test_num_isinf        },
         4616  +     { "sqlite4_num_isnan",             (Tcl_CmdProc*)test_num_isnan        },
  4409   4617     };
  4410   4618     static struct {
  4411   4619        char *zName;
  4412   4620        Tcl_ObjCmdProc *xProc;
  4413   4621        void *clientData;
  4414   4622     } aObjCmd[] = {
  4415   4623        { "sqlite4_connection_pointer",    get_sqlite_pointer, 0 },