SQLite

Check-in [3ff2f1cdc9]
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
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 3ff2f1cdc9c57bca56de6cdc0ad5edc95b0606a0
User & Date: drh 2008-05-11 11:07:07.000
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: 88e12caca9 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: 3ff2f1cdc9 user: drh tags: trunk)
2008-05-09
19:38
Fix leaked filename in case DosOpen() fails. (CVS 5115) (check-in: ecc6c73906 user: pweilbacher tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/sqlite.h.in.
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
** on how SQLite interfaces are suppose to operate.
**
** The name of this file under configuration management is "sqlite.h.in".
** The makefile makes some minor changes to this file (such as inserting
** the version number) and changes its name to "sqlite3.h" as
** part of the build process.
**
** @(#) $Id: sqlite.h.in,v 1.310 2008/04/27 22:48:05 drh Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.







|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
** on how SQLite interfaces are suppose to operate.
**
** The name of this file under configuration management is "sqlite.h.in".
** The makefile makes some minor changes to this file (such as inserting
** the version number) and changes its name to "sqlite3.h" as
** part of the build process.
**
** @(#) $Id: sqlite.h.in,v 1.311 2008/05/11 11:07:07 drh Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
**          SQLite was compiled with its mutexes enabled or zero
**          if SQLite was compiled with mutexes disabled.
*/
int sqlite3_threadsafe(void);

/*
** CAPI3REF: Database Connection Handle {F12000}
** KEYWORDS: {database connection}
**
** Each open SQLite database is represented by pointer to an instance of the
** opaque structure named "sqlite3".  It is useful to think of an sqlite3
** pointer as an object.  The [sqlite3_open()], [sqlite3_open16()], and
** [sqlite3_open_v2()] interfaces are its constructors
** and [sqlite3_close()] is its destructor.  There are many other interfaces
** (such as [sqlite3_prepare_v2()], [sqlite3_create_function()], and







|







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
**          SQLite was compiled with its mutexes enabled or zero
**          if SQLite was compiled with mutexes disabled.
*/
int sqlite3_threadsafe(void);

/*
** CAPI3REF: Database Connection Handle {F12000}
** KEYWORDS: {database connection} {database connections}
**
** Each open SQLite database is represented by pointer to an instance of the
** opaque structure named "sqlite3".  It is useful to think of an sqlite3
** pointer as an object.  The [sqlite3_open()], [sqlite3_open16()], and
** [sqlite3_open_v2()] interfaces are its constructors
** and [sqlite3_close()] is its destructor.  There are many other interfaces
** (such as [sqlite3_prepare_v2()], [sqlite3_create_function()], and
Changes to src/test1.c.
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing all sorts of SQLite interfaces.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test1.c,v 1.301 2008/05/05 11:33:48 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>

/*







|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing all sorts of SQLite interfaces.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test1.c,v 1.302 2008/05/11 11:07:07 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>

/*
2610
2611
2612
2613
2614
2615
2616


















2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632


2633
2634
2635

2636




2637
2638
2639
2640
2641
2642
2643
2644
  int objc,
  Tcl_Obj *CONST objv[]
){
  sqlite3_stmt *pStmt;
  int idx;
  double value;
  int rc;



















  if( objc!=4 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"",
        Tcl_GetStringFromObj(objv[0], 0), " STMT N VALUE", 0);
    return TCL_ERROR;
  }

  if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
  if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR;

  /* Intercept the string "NaN" and generate a NaN value for it.
  ** All other strings are passed through to Tcl_GetDoubleFromObj().
  ** Tcl_GetDoubleFromObj() should understand "NaN" but some versions
  ** contain a bug.
  */
  if( strcmp(Tcl_GetString(objv[3]), "NaN")==0 ){


    sqlite3_int64 i;
    i = 0xfff80000;
    i <<= 32;

    value = *(double*)(char*)&i;




  }else if( Tcl_GetDoubleFromObj(interp, objv[3], &value) ){
    return TCL_ERROR;
  }
  rc = sqlite3_bind_double(pStmt, idx, value);
  if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR;
  if( rc!=SQLITE_OK ){
    return TCL_ERROR;
  }







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















|
>
>
|
|
|
>
|
>
>
>
>
|







2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
  int objc,
  Tcl_Obj *CONST objv[]
){
  sqlite3_stmt *pStmt;
  int idx;
  double value;
  int rc;
  const char *zVal;
  int i;
  static const struct {
    const char *zName;     /* Name of the special floating point value */
    unsigned int iUpper;   /* Upper 32 bits */
    unsigned int iLower;   /* Lower 32 bits */
  } aSpecialFp[] = {
    {  "NaN",      0x7fffffff, 0xffffffff },
    {  "SNaN",     0x7ff7ffff, 0xffffffff },
    {  "-NaN",     0xffffffff, 0xffffffff },
    {  "-SNaN",    0xfff7ffff, 0xffffffff },
    {  "+Inf",     0x7ff00000, 0x00000000 },
    {  "-Inf",     0xfff00000, 0x00000000 },
    {  "Epsilon",  0x00000000, 0x00000001 },
    {  "-Epsilon", 0x80000000, 0x00000001 },
    {  "NaN0",     0x7ff80000, 0x00000000 },
    {  "-NaN0",    0xfff80000, 0x00000000 },
  };

  if( objc!=4 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"",
        Tcl_GetStringFromObj(objv[0], 0), " STMT N VALUE", 0);
    return TCL_ERROR;
  }

  if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
  if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR;

  /* Intercept the string "NaN" and generate a NaN value for it.
  ** All other strings are passed through to Tcl_GetDoubleFromObj().
  ** Tcl_GetDoubleFromObj() should understand "NaN" but some versions
  ** contain a bug.
  */
  zVal = Tcl_GetString(objv[3]);
  for(i=0; i<sizeof(aSpecialFp)/sizeof(aSpecialFp[0]); i++){
    if( strcmp(aSpecialFp[i].zName, zVal)==0 ){
      sqlite3_uint64 x;
      x = aSpecialFp[i].iUpper;
      x <<= 32;
      x |= aSpecialFp[i].iLower;
      value = *(double*)(char*)&x;
      break;
    }
  }
  if( i>=sizeof(aSpecialFp)/sizeof(aSpecialFp[0]) &&
         Tcl_GetDoubleFromObj(interp, objv[3], &value) ){
    return TCL_ERROR;
  }
  rc = sqlite3_bind_double(pStmt, idx, value);
  if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR;
  if( rc!=SQLITE_OK ){
    return TCL_ERROR;
  }
Changes to src/util.c.
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
**
*************************************************************************
** Utility functions used throughout sqlite.
**
** This file contains functions for allocating memory, comparing
** strings, and stuff like that.
**
** $Id: util.c,v 1.227 2008/05/09 13:47:59 drh Exp $
*/
#include "sqliteInt.h"
#include <stdarg.h>
#include <ctype.h>


/*







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
**
*************************************************************************
** Utility functions used throughout sqlite.
**
** This file contains functions for allocating memory, comparing
** strings, and stuff like that.
**
** $Id: util.c,v 1.228 2008/05/11 11:07:07 drh Exp $
*/
#include "sqliteInt.h"
#include <stdarg.h>
#include <ctype.h>


/*
258
259
260
261
262
263
264

265
266
267
268
269
270



271
272
273
274

275
276
277
278






279

280
281


282
283
284
285
286
287
288
** for SQL.  So this routine always uses "." regardless of locale.
*/
int sqlite3AtoF(const char *z, double *pResult){
#ifndef SQLITE_OMIT_FLOATING_POINT
  int sign = 1;
  const char *zBegin = z;
  LONGDOUBLE_TYPE v1 = 0.0;

  while( isspace(*(u8*)z) ) z++;
  if( *z=='-' ){
    sign = -1;
    z++;
  }else if( *z=='+' ){
    z++;



  }
  while( isdigit(*(u8*)z) ){
    v1 = v1*10.0 + (*z - '0');
    z++;

  }
  if( *z=='.' ){
    LONGDOUBLE_TYPE divisor = 1.0;
    z++;






    while( isdigit(*(u8*)z) ){

      v1 = v1*10.0 + (*z - '0');
      divisor *= 10.0;


      z++;
    }
    v1 /= divisor;
  }
  if( *z=='e' || *z=='E' ){
    int esign = 1;
    int eval = 0;







>






>
>
>




>




>
>
>
>
>
>

>
|
|
>
>







258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
** for SQL.  So this routine always uses "." regardless of locale.
*/
int sqlite3AtoF(const char *z, double *pResult){
#ifndef SQLITE_OMIT_FLOATING_POINT
  int sign = 1;
  const char *zBegin = z;
  LONGDOUBLE_TYPE v1 = 0.0;
  int nSignificant = 0;
  while( isspace(*(u8*)z) ) z++;
  if( *z=='-' ){
    sign = -1;
    z++;
  }else if( *z=='+' ){
    z++;
  }
  while( z[0]=='0' ){
    z++;
  }
  while( isdigit(*(u8*)z) ){
    v1 = v1*10.0 + (*z - '0');
    z++;
    nSignificant++;
  }
  if( *z=='.' ){
    LONGDOUBLE_TYPE divisor = 1.0;
    z++;
    if( nSignificant==0 ){
      while( z[0]=='0' ){
        divisor *= 10.0;
        z++;
      }
    }
    while( isdigit(*(u8*)z) ){
      if( nSignificant<18 ){
        v1 = v1*10.0 + (*z - '0');
        divisor *= 10.0;
        nSignificant++;
      }
      z++;
    }
    v1 /= divisor;
  }
  if( *z=='e' || *z=='E' ){
    int esign = 1;
    int eval = 0;
Changes to test/nan.test.
10
11
12
13
14
15
16




17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41



42
43
44
45


46
47
48
49


50
51
52


















53
54
55
56
57
58
59
60
61
62
63


64
65

66
67
68
69
70
71
72
#***********************************************************************
#
# Ticket #3060
#
# Make sure IEEE floating point NaN values are handled properly.
# SQLite should always convert NaN into NULL.
#




# $Id: nan.test,v 1.2 2008/05/01 18:01:47 drh Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# The ascii->float conversion routine in SQLite converts all digits
# of a number to a long long double.  Then it divids by 10**N where
# N is the number of digits to the right of the decimal point.  If
# both the full number and 10**N are +Inf we will get +Inf/+Inf which
# is NaN.
#
unset -nocomplain nan
set nan 9.[string repeat 9 5000]

unset -nocomplain inf
set inf [string repeat 9 5000].0

do_test nan-1.1 {
  db eval {
    PRAGMA auto_vacuum=OFF;
    PRAGMA page_size=1024;
    CREATE TABLE t1(x FLOAT);
  }
  db eval "INSERT INTO t1 VALUES($nan)"



  db eval {SELECT x, typeof(x) FROM t1}
} {{} null}
do_test nan-1.2 {
  db eval "INSERT INTO t1 VALUES($inf)"


  db eval {SELECT x, typeof(x) FROM t1}
} {{} null inf real}
do_test nan-1.3 {
  db eval "INSERT INTO t1 VALUES(-$inf)"


  db eval {SELECT x, typeof(x) FROM t1}
} {{} null inf real -inf real}
do_test nan-1.4 {


















  db eval {
    UPDATE t1 SET x=x-x;
    SELECT x, typeof(x) FROM t1;
  }
} {{} null {} null {} null}

do_test nan-2.1 {
  db eval {
    DELETE FROM T1;
  }
  db eval "INSERT INTO t1 VALUES('$nan')"


  db eval {SELECT x, typeof(x) FROM t1}
} {{} null}


# SQLite always converts NaN into NULL so it is not possible to write
# a NaN value into the database file using SQLite.  The following series
# of tests writes a normal floating point value (0.5) into the database,
# then writes directly into the database file to change the 0.5 into NaN.
# Then it reads the value of the database to verify it is converted into
# NULL.







>
>
>
>
|





<
<
<
<
<
<
<
<
<
<
<







|
>
>
>



|
>
>



|
>
>



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




|





|
>
>


>







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26











27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#***********************************************************************
#
# Ticket #3060
#
# Make sure IEEE floating point NaN values are handled properly.
# SQLite should always convert NaN into NULL.
#
# Also verify that the decimal to IEEE754 binary conversion routines
# correctly generate 0.0, +Inf, and -Inf as appropriate for numbers
# out of range.
#
# $Id: nan.test,v 1.3 2008/05/11 11:07:07 drh Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl













do_test nan-1.1 {
  db eval {
    PRAGMA auto_vacuum=OFF;
    PRAGMA page_size=1024;
    CREATE TABLE t1(x FLOAT);
  }
  set ::STMT [sqlite3_prepare db "INSERT INTO t1 VALUES(?)" -1 TAIL]
  sqlite3_bind_double $::STMT 1 NaN
  sqlite3_step $::STMT
  sqlite3_reset $::STMT
  db eval {SELECT x, typeof(x) FROM t1}
} {{} null}
do_test nan-1.2 {
  sqlite3_bind_double $::STMT 1 +Inf
  sqlite3_step $::STMT
  sqlite3_reset $::STMT
  db eval {SELECT x, typeof(x) FROM t1}
} {{} null inf real}
do_test nan-1.3 {
  sqlite3_bind_double $::STMT 1 -Inf
  sqlite3_step $::STMT
  sqlite3_reset $::STMT
  db eval {SELECT x, typeof(x) FROM t1}
} {{} null inf real -inf real}
do_test nan-1.4 {
  sqlite3_bind_double $::STMT 1 -NaN
  sqlite3_step $::STMT
  sqlite3_reset $::STMT
  db eval {SELECT x, typeof(x) FROM t1}
} {{} null inf real -inf real {} null}
do_test nan-1.5 {
  sqlite3_bind_double $::STMT 1 NaN0
  sqlite3_step $::STMT
  sqlite3_reset $::STMT
  db eval {SELECT x, typeof(x) FROM t1}
} {{} null inf real -inf real {} null {} null}
do_test nan-1.5 {
  sqlite3_bind_double $::STMT 1 -NaN0
  sqlite3_step $::STMT
  sqlite3_reset $::STMT
  db eval {SELECT x, typeof(x) FROM t1}
} {{} null inf real -inf real {} null {} null {} null}
do_test nan-1.6 {
  db eval {
    UPDATE t1 SET x=x-x;
    SELECT x, typeof(x) FROM t1;
  }
} {{} null {} null {} null {} null {} null {} null}

do_test nan-2.1 {
  db eval {
    DELETE FROM T1;
  }
  sqlite3_bind_double $::STMT 1 NaN
  sqlite3_step $::STMT
  sqlite3_reset $::STMT
  db eval {SELECT x, typeof(x) FROM t1}
} {{} null}
sqlite3_finalize $::STMT

# SQLite always converts NaN into NULL so it is not possible to write
# a NaN value into the database file using SQLite.  The following series
# of tests writes a normal floating point value (0.5) into the database,
# then writes directly into the database file to change the 0.5 into NaN.
# Then it reads the value of the database to verify it is converted into
# NULL.
88
89
90
91
92
93
94


















95





























































































96
} {0.5 real}
do_test nan-3.3 {
  db close
  hexio_write test.db 2040 FFF8000000000000
  sqlite3 db test.db
  db eval {SELECT x, typeof(x) FROM t1}
} {{} null}
















































































































finish_test







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

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

109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
} {0.5 real}
do_test nan-3.3 {
  db close
  hexio_write test.db 2040 FFF8000000000000
  sqlite3 db test.db
  db eval {SELECT x, typeof(x) FROM t1}
} {{} null}
do_test nan-3.4 {
  db close
  hexio_write test.db 2040 7FF8000000000000
  sqlite3 db test.db
  db eval {SELECT x, typeof(x) FROM t1}
} {{} null}
do_test nan-3.5 {
  db close
  hexio_write test.db 2040 FFFFFFFFFFFFFFFF
  sqlite3 db test.db
  db eval {SELECT x, typeof(x) FROM t1}
} {{} null}
do_test nan-3.6 {
  db close
  hexio_write test.db 2040 7FFFFFFFFFFFFFFF
  sqlite3 db test.db
  db eval {SELECT x, typeof(x) FROM t1}
} {{} null}

# Verify that the sqlite3AtoF routine is able to handle extreme
# numbers.
#
do_test nan-4.1 {
  db eval {DELETE FROM t1}
  db eval "INSERT INTO t1 VALUES([string repeat 9 307].0)"
  db eval {SELECT x, typeof(x) FROM t1}
} {1e+307 real}
do_test nan-4.2 {
  db eval {DELETE FROM t1}
  db eval "INSERT INTO t1 VALUES([string repeat 9 308].0)"
  db eval {SELECT x, typeof(x) FROM t1}
} {1e+308 real}
do_test nan-4.3 {
  db eval {DELETE FROM t1}
  db eval "INSERT INTO t1 VALUES([string repeat 9 309].0)"
  db eval {SELECT x, typeof(x) FROM t1}
} {inf real}
do_test nan-4.4 {
  db eval {DELETE FROM t1}
  db eval "INSERT INTO t1 VALUES(-[string repeat 9 307].0)"
  db eval {SELECT x, typeof(x) FROM t1}
} {-1e+307 real}
do_test nan-4.5 {
  db eval {DELETE FROM t1}
  db eval "INSERT INTO t1 VALUES(-[string repeat 9 308].0)"
  db eval {SELECT x, typeof(x) FROM t1}
} {-1e+308 real}
do_test nan-4.6 {
  db eval {DELETE FROM t1}
  db eval "INSERT INTO t1 VALUES(-[string repeat 9 309].0)"
  db eval {SELECT x, typeof(x) FROM t1}
} {-inf real}
do_test nan-4.7 {
  db eval {DELETE FROM t1}
  set big -[string repeat 0 10000][string repeat 9 308].[string repeat 0 10000]
  db eval "INSERT INTO t1 VALUES($big)"
  db eval {SELECT x, typeof(x) FROM t1}
} {-1e+308 real}
do_test nan-4.8 {
  db eval {DELETE FROM t1}
  set big [string repeat 0 10000][string repeat 9 308].[string repeat 0 10000]
  db eval "INSERT INTO t1 VALUES($big)"
  db eval {SELECT x, typeof(x) FROM t1}
} {1e+308 real}


do_test nan-4.10 {
  db eval {DELETE FROM t1}
  db eval "INSERT INTO t1 VALUES(1234.5[string repeat 0 10000]12345)"
  db eval {SELECT x, typeof(x) FROM t1}
} {1234.5 real}
do_test nan-4.11 {
  db eval {DELETE FROM t1}
  db eval "INSERT INTO t1 VALUES(-1234.5[string repeat 0 10000]12345)"
  db eval {SELECT x, typeof(x) FROM t1}
} {-1234.5 real}
do_test nan-4.12 {
  db eval {DELETE FROM t1}
  set small [string repeat 0 10000].[string repeat 0 323][string repeat 9 10000]
  db eval "INSERT INTO t1 VALUES($small)"
  db eval {SELECT x, typeof(x) FROM t1}
} {9.88131291682493e-324 real}
do_test nan-4.13 {
  db eval {DELETE FROM t1}
  set small [string repeat 0 10000].[string repeat 0 324][string repeat 9 10000]
  db eval "INSERT INTO t1 VALUES($small)"
  db eval {SELECT x, typeof(x) FROM t1}
} {0.0 real}
do_test nan-4.14 {
  db eval {DELETE FROM t1}
  set small \
      -[string repeat 0 10000].[string repeat 0 323][string repeat 9 10000]
  db eval "INSERT INTO t1 VALUES($small)"
  db eval {SELECT x, typeof(x) FROM t1}
} {-9.88131291682493e-324 real}
do_test nan-4.15 {
  db eval {DELETE FROM t1}
  set small \
      -[string repeat 0 10000].[string repeat 0 324][string repeat 9 10000]
  db eval "INSERT INTO t1 VALUES($small)"
  db eval {SELECT x, typeof(x) FROM t1}
} {0.0 real}

do_test nan-4.20 {
  db eval {DELETE FROM t1}
  set big [string repeat 9 10000].0e-9000
  db eval "INSERT INTO t1 VALUES($big)"
  db eval {SELECT x, typeof(x) FROM t1}
} {{} null}



finish_test