L.S.

I've noticed that on a platform for which SQLITE_MIXED_ENDIAN_64BIT_FLOAT 
needs to be defined, Sqlite exposes itself to any difference between the 
floating point implementation in Sqlite as opposed to the one used by the 
underlying platform. To me this seems like something that needs to be 
avoided. This post explains what's going on and contains a patch to correct 
the behaviour.

The platform for which I was noticing this is an arm-based AML7100 handheld 
barcodescanner, specifically the processor involved is a StrongArm 1110 with 
a v4 core. Most likely also due to the hardware setup, it is swapping quads. 
In earlier versions of Sqlite, this swapping was noticed and solved, see also 
the following post:

http://www.mail-archive.com/sqlite-users@sqlite.org/msg09745.html

When I picked up the latest version of Sqlite, I was happy to see that the 
newer version of the code had adapted the idea and allowed for 
SQLITE_MIXED_ENDIAN_64BIT_FLOAT to be set. The comments in the code for this 
macro hint that this swapping of quads is a gcc-only thing (newer versions 
should not be exposed to this), which might be true for the ARM7 family, but 
it could mention a bit more specifically that various other Arm-processors 
seem to allow for both mixed endian modes as well as mixed 'wiring' of 
memory, for which certain choices may result in such a mixed-endian setting. 
I'm far from an Arm-expert though, so I'll leave the details to someone 
else ;)

Anyway, when this macro was set, reading floats from databases (even those 
produced on for example an i386 architecture) worked properly, but writing 
and re-reading data from the database failed (the reread values seemed to 
make no sense at all). After recompiling Sqlite with debug enabled, 
assertions in the code like the one below also failed

vbdeaux.c:1953
      static const u64 t1 = ((u64)0x3ff00000)<<32;
      static const double r1 = 1.0;
      double r2 = r1;
      swapMixedEndianFloat(r2);
      assert( sizeof(r2)==sizeof(t1) && memcmp(&r2, &t1, sizeof(r1))==0 );


Further investigation revealed the cause for this and it is kinda nasty....

This particular processor lacks a FP-unit (like many other Arm-processors) and 
therefor depends on either the FP-support in the kernel or on using 
soft-float from gcc. The latter is often not an easy choice when 
crosscompiling, so in my case too, the kernel's FP-support was being used. 
This basically means that gcc generates FP-opcodes that will raise an 
exception on the cpu, which the kernel will catch, after which it'll 
interpret and reroute the instruction to it's FP-engine. This obviously has a 
downside efficiency-wise, but so be it.

The (pre-installed) kernel that was being used here (2.4.17) is offering two 
kinds of math emulation (later kernels have more): CONFIG_FPE_NWFPE and 
CONFIG_FPE_FASTFPE. Although NWFPE is default, this particular kernel had 
been configured with FASTFPE. The interesting part from the docs on 
CONFIG_FPE_FASTFPE is this: "This is an experimental much faster emulator 
which has only 32 bit precision for the mantissa."

So, I was getting burned by the cut down precision of doubles used in the 
FP-engine of the kernel. Though this would normally not show up, the 
quad-swapped values in the current version of sqlite are actually treated AS 
DOUBLES on various places in the code. When this happens, the kernel 
FP-emulator will actually touch these values, but now the lesser precision of 
the double *do* work out in an unpredictable (kinda) way, simply because the 
quads are swapped......

This behaviour was verified by looking at the actual values used on various 
places in the code:

* the FP64 representation of 1.0 is 3ff0 0000 0000 0000

* upon an 'insert into <table> values (1.0)' I see that the value Sqlite tries 
to insert is 3ff0 0000 0040 0000........ the '3ff' is the sign and exponent 
part, the rest of the 13 * 4 bits are the fraction, but since the fast 
floating point emulator in the kernel only has an accuracy of under 32 bits, 
we see the '4' popping up

* after swapping quads, the value that is actually going to be inserted by 
sqlite is 3fe0 0000 0040 0000; the reason for this being that the second 'f' 
that is changed into 'e' after swapping quads is on the exact same spot as 
where the '4' was before swapping: at the end of the accuracy range of the 
fast floating point emulator

* obviously, this is also the reason why the assert fails when --enable-debug 
is being used, it's doing this exact same test and it notices the difference 
between 03ff and 03fe


So, to wrap this up, because floats are treated as floats after swapping as 
well, Sqlite is basically exposing itself to any quirk in FP engines on any 
platform for which the mixed endian macro needs to be used, which in my 
humble opinion is not a Good Thing(tm) ;)

I'm unsure how D. Richard Hipp would prefer to handle this, I reported these 
findings directly to him before posting here, but got no response ;(

Specifically I didn't want to touch the assert tests nor his comment in the 
current code:

** (later):  It is reported to me that the mixed-endian problem
** on ARM7 is an issue with GCC, not with the ARM7 chip.  It seems
** that early versions of GCC stored the two words of a 64-bit
** float in the wrong order.  And that error has been propagated
** ever since.  The blame is not necessarily with GCC, though.
** GCC might have just copying the problem from a prior compiler.
** I am also told that newer versions of GCC that follow a different
** ABI get the byte order right.

As described earlier, I think there are a few non-ARM7 platforms out there for 
which the mixed endian thingy really is a hardware issue. The fact that my 
use of a gcc v4.1.1 based crosscompiler didn't change anything seems to 
confirm this. This fact could be mentioned in the comment too.


So, the patch below focuses on the cause of the problem only, it's proper 
working is verified with both a gcc-2.95.3 based crosscompiler as well as a 
gcc-4.1.1 based crosscompiler. The things that remain is correcting the 
assert in sqlite3VdbeSerialGet() that gets triggered when 
using --enable-debug and correcting the comments.



diff -u -r sqlite-3.4.2-orig/src/vdbeaux.c sqlite-3.4.2/src/vdbeaux.c
--- sqlite-3.4.2-orig/src/vdbeaux.c     2007-08-27 16:46:46.000000000 +0200
+++ sqlite-3.4.2/src/vdbeaux.c  2007-08-30 12:13:14.000000000 +0200
@@ -1815,9 +1815,9 @@
 ** floating point values is correct.
 */
 #ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT
-static double floatSwap(double in){
+static u64 floatSwap(u64 in){
   union {
-    double r;
+    u64 r;
     u32 i[2];
   } u;
   u32 t;
@@ -1861,8 +1861,8 @@
     int i;
     if( serial_type==7 ){
       assert( sizeof(v)==sizeof(pMem->r) );
-      swapMixedEndianFloat(pMem->r);
       memcpy(&v, &pMem->r, sizeof(v));
+      swapMixedEndianFloat(v);
     }else{
       v = pMem->u.i;
     }
@@ -1965,8 +1965,8 @@
         pMem->flags = MEM_Int;
       }else{
         assert( sizeof(x)==8 && sizeof(pMem->r)==8 );
+        swapMixedEndianFloat(x);
         memcpy(&pMem->r, &x, sizeof(x));
-        swapMixedEndianFloat(pMem->r);
         pMem->flags = MEM_Real;
       }
       return 8;







Best,





Frank.

-----------------------------------------------------------------------------
To unsubscribe, send email to [EMAIL PROTECTED]
-----------------------------------------------------------------------------

Reply via email to