/ Check-in [3ff2f1cd]
Login

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

Overview
Comment:New test cases to verify that SQLite handles bound NaN, +Inf, and -Inf floating point values correctly. Improvements to the text->real conversion routine so that it generates +Inf and -Inf at appropriate times. Tickets #3101 and #3060. (CVS 5116)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 3ff2f1cdc9c57bca56de6cdc0ad5edc95b0606a0
User & Date: drh 2008-05-11 11:07:07
Context
2008-05-11
17:22
Use memcpy() instead of casting to copy the content of a long long int into a double. Ticket #3101. (CVS 5117) check-in: 88e12cac user: drh tags: trunk
11:07
New test cases to verify that SQLite handles bound NaN, +Inf, and -Inf floating point values correctly. Improvements to the text->real conversion routine so that it generates +Inf and -Inf at appropriate times. Tickets #3101 and #3060. (CVS 5116) check-in: 3ff2f1cd user: drh tags: trunk
2008-05-09
19:38
Fix leaked filename in case DosOpen() fails. (CVS 5115) check-in: ecc6c739 user: pweilbacher tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/sqlite.h.in.

    26     26   ** on how SQLite interfaces are suppose to operate.
    27     27   **
    28     28   ** The name of this file under configuration management is "sqlite.h.in".
    29     29   ** The makefile makes some minor changes to this file (such as inserting
    30     30   ** the version number) and changes its name to "sqlite3.h" as
    31     31   ** part of the build process.
    32     32   **
    33         -** @(#) $Id: sqlite.h.in,v 1.310 2008/04/27 22:48:05 drh Exp $
           33  +** @(#) $Id: sqlite.h.in,v 1.311 2008/05/11 11:07:07 drh Exp $
    34     34   */
    35     35   #ifndef _SQLITE3_H_
    36     36   #define _SQLITE3_H_
    37     37   #include <stdarg.h>     /* Needed for the definition of va_list */
    38     38   
    39     39   /*
    40     40   ** Make sure we can call this stuff from C++.
................................................................................
   151    151   **          SQLite was compiled with its mutexes enabled or zero
   152    152   **          if SQLite was compiled with mutexes disabled.
   153    153   */
   154    154   int sqlite3_threadsafe(void);
   155    155   
   156    156   /*
   157    157   ** CAPI3REF: Database Connection Handle {F12000}
   158         -** KEYWORDS: {database connection}
          158  +** KEYWORDS: {database connection} {database connections}
   159    159   **
   160    160   ** Each open SQLite database is represented by pointer to an instance of the
   161    161   ** opaque structure named "sqlite3".  It is useful to think of an sqlite3
   162    162   ** pointer as an object.  The [sqlite3_open()], [sqlite3_open16()], and
   163    163   ** [sqlite3_open_v2()] interfaces are its constructors
   164    164   ** and [sqlite3_close()] is its destructor.  There are many other interfaces
   165    165   ** (such as [sqlite3_prepare_v2()], [sqlite3_create_function()], and

Changes to src/test1.c.

     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** Code for testing all sorts of SQLite interfaces.  This code
    13     13   ** is not included in the SQLite library.  It is used for automated
    14     14   ** testing of the SQLite library.
    15     15   **
    16         -** $Id: test1.c,v 1.301 2008/05/05 11:33:48 danielk1977 Exp $
           16  +** $Id: test1.c,v 1.302 2008/05/11 11:07:07 drh Exp $
    17     17   */
    18     18   #include "sqliteInt.h"
    19     19   #include "tcl.h"
    20     20   #include <stdlib.h>
    21     21   #include <string.h>
    22     22   
    23     23   /*
................................................................................
  2610   2610     int objc,
  2611   2611     Tcl_Obj *CONST objv[]
  2612   2612   ){
  2613   2613     sqlite3_stmt *pStmt;
  2614   2614     int idx;
  2615   2615     double value;
  2616   2616     int rc;
         2617  +  const char *zVal;
         2618  +  int i;
         2619  +  static const struct {
         2620  +    const char *zName;     /* Name of the special floating point value */
         2621  +    unsigned int iUpper;   /* Upper 32 bits */
         2622  +    unsigned int iLower;   /* Lower 32 bits */
         2623  +  } aSpecialFp[] = {
         2624  +    {  "NaN",      0x7fffffff, 0xffffffff },
         2625  +    {  "SNaN",     0x7ff7ffff, 0xffffffff },
         2626  +    {  "-NaN",     0xffffffff, 0xffffffff },
         2627  +    {  "-SNaN",    0xfff7ffff, 0xffffffff },
         2628  +    {  "+Inf",     0x7ff00000, 0x00000000 },
         2629  +    {  "-Inf",     0xfff00000, 0x00000000 },
         2630  +    {  "Epsilon",  0x00000000, 0x00000001 },
         2631  +    {  "-Epsilon", 0x80000000, 0x00000001 },
         2632  +    {  "NaN0",     0x7ff80000, 0x00000000 },
         2633  +    {  "-NaN0",    0xfff80000, 0x00000000 },
         2634  +  };
  2617   2635   
  2618   2636     if( objc!=4 ){
  2619   2637       Tcl_AppendResult(interp, "wrong # args: should be \"",
  2620   2638           Tcl_GetStringFromObj(objv[0], 0), " STMT N VALUE", 0);
  2621   2639       return TCL_ERROR;
  2622   2640     }
  2623   2641   
................................................................................
  2625   2643     if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR;
  2626   2644   
  2627   2645     /* Intercept the string "NaN" and generate a NaN value for it.
  2628   2646     ** All other strings are passed through to Tcl_GetDoubleFromObj().
  2629   2647     ** Tcl_GetDoubleFromObj() should understand "NaN" but some versions
  2630   2648     ** contain a bug.
  2631   2649     */
  2632         -  if( strcmp(Tcl_GetString(objv[3]), "NaN")==0 ){
  2633         -    sqlite3_int64 i;
  2634         -    i = 0xfff80000;
  2635         -    i <<= 32;
  2636         -    value = *(double*)(char*)&i;
  2637         -  }else if( Tcl_GetDoubleFromObj(interp, objv[3], &value) ){
         2650  +  zVal = Tcl_GetString(objv[3]);
         2651  +  for(i=0; i<sizeof(aSpecialFp)/sizeof(aSpecialFp[0]); i++){
         2652  +    if( strcmp(aSpecialFp[i].zName, zVal)==0 ){
         2653  +      sqlite3_uint64 x;
         2654  +      x = aSpecialFp[i].iUpper;
         2655  +      x <<= 32;
         2656  +      x |= aSpecialFp[i].iLower;
         2657  +      value = *(double*)(char*)&x;
         2658  +      break;
         2659  +    }
         2660  +  }
         2661  +  if( i>=sizeof(aSpecialFp)/sizeof(aSpecialFp[0]) &&
         2662  +         Tcl_GetDoubleFromObj(interp, objv[3], &value) ){
  2638   2663       return TCL_ERROR;
  2639   2664     }
  2640   2665     rc = sqlite3_bind_double(pStmt, idx, value);
  2641   2666     if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR;
  2642   2667     if( rc!=SQLITE_OK ){
  2643   2668       return TCL_ERROR;
  2644   2669     }

Changes to src/util.c.

    10     10   **
    11     11   *************************************************************************
    12     12   ** Utility functions used throughout sqlite.
    13     13   **
    14     14   ** This file contains functions for allocating memory, comparing
    15     15   ** strings, and stuff like that.
    16     16   **
    17         -** $Id: util.c,v 1.227 2008/05/09 13:47:59 drh Exp $
           17  +** $Id: util.c,v 1.228 2008/05/11 11:07:07 drh Exp $
    18     18   */
    19     19   #include "sqliteInt.h"
    20     20   #include <stdarg.h>
    21     21   #include <ctype.h>
    22     22   
    23     23   
    24     24   /*
................................................................................
   258    258   ** for SQL.  So this routine always uses "." regardless of locale.
   259    259   */
   260    260   int sqlite3AtoF(const char *z, double *pResult){
   261    261   #ifndef SQLITE_OMIT_FLOATING_POINT
   262    262     int sign = 1;
   263    263     const char *zBegin = z;
   264    264     LONGDOUBLE_TYPE v1 = 0.0;
          265  +  int nSignificant = 0;
   265    266     while( isspace(*(u8*)z) ) z++;
   266    267     if( *z=='-' ){
   267    268       sign = -1;
   268    269       z++;
   269    270     }else if( *z=='+' ){
   270    271       z++;
          272  +  }
          273  +  while( z[0]=='0' ){
          274  +    z++;
   271    275     }
   272    276     while( isdigit(*(u8*)z) ){
   273    277       v1 = v1*10.0 + (*z - '0');
   274    278       z++;
          279  +    nSignificant++;
   275    280     }
   276    281     if( *z=='.' ){
   277    282       LONGDOUBLE_TYPE divisor = 1.0;
   278    283       z++;
          284  +    if( nSignificant==0 ){
          285  +      while( z[0]=='0' ){
          286  +        divisor *= 10.0;
          287  +        z++;
          288  +      }
          289  +    }
   279    290       while( isdigit(*(u8*)z) ){
   280         -      v1 = v1*10.0 + (*z - '0');
   281         -      divisor *= 10.0;
          291  +      if( nSignificant<18 ){
          292  +        v1 = v1*10.0 + (*z - '0');
          293  +        divisor *= 10.0;
          294  +        nSignificant++;
          295  +      }
   282    296         z++;
   283    297       }
   284    298       v1 /= divisor;
   285    299     }
   286    300     if( *z=='e' || *z=='E' ){
   287    301       int esign = 1;
   288    302       int eval = 0;

Changes to test/nan.test.

    10     10   #***********************************************************************
    11     11   #
    12     12   # Ticket #3060
    13     13   #
    14     14   # Make sure IEEE floating point NaN values are handled properly.
    15     15   # SQLite should always convert NaN into NULL.
    16     16   #
    17         -# $Id: nan.test,v 1.2 2008/05/01 18:01:47 drh Exp $
           17  +# Also verify that the decimal to IEEE754 binary conversion routines
           18  +# correctly generate 0.0, +Inf, and -Inf as appropriate for numbers
           19  +# out of range.
           20  +#
           21  +# $Id: nan.test,v 1.3 2008/05/11 11:07:07 drh Exp $
    18     22   #
    19     23   
    20     24   set testdir [file dirname $argv0]
    21     25   source $testdir/tester.tcl
    22     26   
    23         -# The ascii->float conversion routine in SQLite converts all digits
    24         -# of a number to a long long double.  Then it divids by 10**N where
    25         -# N is the number of digits to the right of the decimal point.  If
    26         -# both the full number and 10**N are +Inf we will get +Inf/+Inf which
    27         -# is NaN.
    28         -#
    29         -unset -nocomplain nan
    30         -set nan 9.[string repeat 9 5000]
    31         -
    32         -unset -nocomplain inf
    33         -set inf [string repeat 9 5000].0
    34     27   
    35     28   do_test nan-1.1 {
    36     29     db eval {
    37     30       PRAGMA auto_vacuum=OFF;
    38     31       PRAGMA page_size=1024;
    39     32       CREATE TABLE t1(x FLOAT);
    40     33     }
    41         -  db eval "INSERT INTO t1 VALUES($nan)"
           34  +  set ::STMT [sqlite3_prepare db "INSERT INTO t1 VALUES(?)" -1 TAIL]
           35  +  sqlite3_bind_double $::STMT 1 NaN
           36  +  sqlite3_step $::STMT
           37  +  sqlite3_reset $::STMT
    42     38     db eval {SELECT x, typeof(x) FROM t1}
    43     39   } {{} null}
    44     40   do_test nan-1.2 {
    45         -  db eval "INSERT INTO t1 VALUES($inf)"
           41  +  sqlite3_bind_double $::STMT 1 +Inf
           42  +  sqlite3_step $::STMT
           43  +  sqlite3_reset $::STMT
    46     44     db eval {SELECT x, typeof(x) FROM t1}
    47     45   } {{} null inf real}
    48     46   do_test nan-1.3 {
    49         -  db eval "INSERT INTO t1 VALUES(-$inf)"
           47  +  sqlite3_bind_double $::STMT 1 -Inf
           48  +  sqlite3_step $::STMT
           49  +  sqlite3_reset $::STMT
    50     50     db eval {SELECT x, typeof(x) FROM t1}
    51     51   } {{} null inf real -inf real}
    52     52   do_test nan-1.4 {
           53  +  sqlite3_bind_double $::STMT 1 -NaN
           54  +  sqlite3_step $::STMT
           55  +  sqlite3_reset $::STMT
           56  +  db eval {SELECT x, typeof(x) FROM t1}
           57  +} {{} null inf real -inf real {} null}
           58  +do_test nan-1.5 {
           59  +  sqlite3_bind_double $::STMT 1 NaN0
           60  +  sqlite3_step $::STMT
           61  +  sqlite3_reset $::STMT
           62  +  db eval {SELECT x, typeof(x) FROM t1}
           63  +} {{} null inf real -inf real {} null {} null}
           64  +do_test nan-1.5 {
           65  +  sqlite3_bind_double $::STMT 1 -NaN0
           66  +  sqlite3_step $::STMT
           67  +  sqlite3_reset $::STMT
           68  +  db eval {SELECT x, typeof(x) FROM t1}
           69  +} {{} null inf real -inf real {} null {} null {} null}
           70  +do_test nan-1.6 {
    53     71     db eval {
    54     72       UPDATE t1 SET x=x-x;
    55     73       SELECT x, typeof(x) FROM t1;
    56     74     }
    57         -} {{} null {} null {} null}
           75  +} {{} null {} null {} null {} null {} null {} null}
    58     76   
    59     77   do_test nan-2.1 {
    60     78     db eval {
    61     79       DELETE FROM T1;
    62     80     }
    63         -  db eval "INSERT INTO t1 VALUES('$nan')"
           81  +  sqlite3_bind_double $::STMT 1 NaN
           82  +  sqlite3_step $::STMT
           83  +  sqlite3_reset $::STMT
    64     84     db eval {SELECT x, typeof(x) FROM t1}
    65     85   } {{} null}
           86  +sqlite3_finalize $::STMT
    66     87   
    67     88   # SQLite always converts NaN into NULL so it is not possible to write
    68     89   # a NaN value into the database file using SQLite.  The following series
    69     90   # of tests writes a normal floating point value (0.5) into the database,
    70     91   # then writes directly into the database file to change the 0.5 into NaN.
    71     92   # Then it reads the value of the database to verify it is converted into
    72     93   # NULL.
................................................................................
    88    109   } {0.5 real}
    89    110   do_test nan-3.3 {
    90    111     db close
    91    112     hexio_write test.db 2040 FFF8000000000000
    92    113     sqlite3 db test.db
    93    114     db eval {SELECT x, typeof(x) FROM t1}
    94    115   } {{} null}
          116  +do_test nan-3.4 {
          117  +  db close
          118  +  hexio_write test.db 2040 7FF8000000000000
          119  +  sqlite3 db test.db
          120  +  db eval {SELECT x, typeof(x) FROM t1}
          121  +} {{} null}
          122  +do_test nan-3.5 {
          123  +  db close
          124  +  hexio_write test.db 2040 FFFFFFFFFFFFFFFF
          125  +  sqlite3 db test.db
          126  +  db eval {SELECT x, typeof(x) FROM t1}
          127  +} {{} null}
          128  +do_test nan-3.6 {
          129  +  db close
          130  +  hexio_write test.db 2040 7FFFFFFFFFFFFFFF
          131  +  sqlite3 db test.db
          132  +  db eval {SELECT x, typeof(x) FROM t1}
          133  +} {{} null}
          134  +
          135  +# Verify that the sqlite3AtoF routine is able to handle extreme
          136  +# numbers.
          137  +#
          138  +do_test nan-4.1 {
          139  +  db eval {DELETE FROM t1}
          140  +  db eval "INSERT INTO t1 VALUES([string repeat 9 307].0)"
          141  +  db eval {SELECT x, typeof(x) FROM t1}
          142  +} {1e+307 real}
          143  +do_test nan-4.2 {
          144  +  db eval {DELETE FROM t1}
          145  +  db eval "INSERT INTO t1 VALUES([string repeat 9 308].0)"
          146  +  db eval {SELECT x, typeof(x) FROM t1}
          147  +} {1e+308 real}
          148  +do_test nan-4.3 {
          149  +  db eval {DELETE FROM t1}
          150  +  db eval "INSERT INTO t1 VALUES([string repeat 9 309].0)"
          151  +  db eval {SELECT x, typeof(x) FROM t1}
          152  +} {inf real}
          153  +do_test nan-4.4 {
          154  +  db eval {DELETE FROM t1}
          155  +  db eval "INSERT INTO t1 VALUES(-[string repeat 9 307].0)"
          156  +  db eval {SELECT x, typeof(x) FROM t1}
          157  +} {-1e+307 real}
          158  +do_test nan-4.5 {
          159  +  db eval {DELETE FROM t1}
          160  +  db eval "INSERT INTO t1 VALUES(-[string repeat 9 308].0)"
          161  +  db eval {SELECT x, typeof(x) FROM t1}
          162  +} {-1e+308 real}
          163  +do_test nan-4.6 {
          164  +  db eval {DELETE FROM t1}
          165  +  db eval "INSERT INTO t1 VALUES(-[string repeat 9 309].0)"
          166  +  db eval {SELECT x, typeof(x) FROM t1}
          167  +} {-inf real}
          168  +do_test nan-4.7 {
          169  +  db eval {DELETE FROM t1}
          170  +  set big -[string repeat 0 10000][string repeat 9 308].[string repeat 0 10000]
          171  +  db eval "INSERT INTO t1 VALUES($big)"
          172  +  db eval {SELECT x, typeof(x) FROM t1}
          173  +} {-1e+308 real}
          174  +do_test nan-4.8 {
          175  +  db eval {DELETE FROM t1}
          176  +  set big [string repeat 0 10000][string repeat 9 308].[string repeat 0 10000]
          177  +  db eval "INSERT INTO t1 VALUES($big)"
          178  +  db eval {SELECT x, typeof(x) FROM t1}
          179  +} {1e+308 real}
          180  +
          181  +
          182  +do_test nan-4.10 {
          183  +  db eval {DELETE FROM t1}
          184  +  db eval "INSERT INTO t1 VALUES(1234.5[string repeat 0 10000]12345)"
          185  +  db eval {SELECT x, typeof(x) FROM t1}
          186  +} {1234.5 real}
          187  +do_test nan-4.11 {
          188  +  db eval {DELETE FROM t1}
          189  +  db eval "INSERT INTO t1 VALUES(-1234.5[string repeat 0 10000]12345)"
          190  +  db eval {SELECT x, typeof(x) FROM t1}
          191  +} {-1234.5 real}
          192  +do_test nan-4.12 {
          193  +  db eval {DELETE FROM t1}
          194  +  set small [string repeat 0 10000].[string repeat 0 323][string repeat 9 10000]
          195  +  db eval "INSERT INTO t1 VALUES($small)"
          196  +  db eval {SELECT x, typeof(x) FROM t1}
          197  +} {9.88131291682493e-324 real}
          198  +do_test nan-4.13 {
          199  +  db eval {DELETE FROM t1}
          200  +  set small [string repeat 0 10000].[string repeat 0 324][string repeat 9 10000]
          201  +  db eval "INSERT INTO t1 VALUES($small)"
          202  +  db eval {SELECT x, typeof(x) FROM t1}
          203  +} {0.0 real}
          204  +do_test nan-4.14 {
          205  +  db eval {DELETE FROM t1}
          206  +  set small \
          207  +      -[string repeat 0 10000].[string repeat 0 323][string repeat 9 10000]
          208  +  db eval "INSERT INTO t1 VALUES($small)"
          209  +  db eval {SELECT x, typeof(x) FROM t1}
          210  +} {-9.88131291682493e-324 real}
          211  +do_test nan-4.15 {
          212  +  db eval {DELETE FROM t1}
          213  +  set small \
          214  +      -[string repeat 0 10000].[string repeat 0 324][string repeat 9 10000]
          215  +  db eval "INSERT INTO t1 VALUES($small)"
          216  +  db eval {SELECT x, typeof(x) FROM t1}
          217  +} {0.0 real}
          218  +
          219  +do_test nan-4.20 {
          220  +  db eval {DELETE FROM t1}
          221  +  set big [string repeat 9 10000].0e-9000
          222  +  db eval "INSERT INTO t1 VALUES($big)"
          223  +  db eval {SELECT x, typeof(x) FROM t1}
          224  +} {{} null}
          225  +
          226  +
    95    227   
    96    228   finish_test