LCOV - code coverage report
Current view: top level - common - serialise-double.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core r Lines: 59 67 88.1 %
Date: 2011-08-21 Functions: 3 3 100.0 %
Branches: 45 58 77.6 %

           Branch data     Line data    Source code
       1                 :            : /* @file serialise-double.cc
       2                 :            :  * @brief functions to serialise and unserialise a double
       3                 :            :  */
       4                 :            : /* Copyright (C) 2006,2007,2008,2009 Olly Betts
       5                 :            :  *
       6                 :            :  * Permission is hereby granted, free of charge, to any person obtaining a copy
       7                 :            :  * of this software and associated documentation files (the "Software"), to
       8                 :            :  * deal in the Software without restriction, including without limitation the
       9                 :            :  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
      10                 :            :  * sell copies of the Software, and to permit persons to whom the Software is
      11                 :            :  * furnished to do so, subject to the following conditions:
      12                 :            :  *
      13                 :            :  * The above copyright notice and this permission notice shall be included in
      14                 :            :  * all copies or substantial portions of the Software.
      15                 :            :  *
      16                 :            :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      17                 :            :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      18                 :            :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      19                 :            :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      20                 :            :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      21                 :            :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
      22                 :            :  * IN THE SOFTWARE.
      23                 :            :  */
      24                 :            : 
      25                 :            : #include <config.h>
      26                 :            : 
      27                 :            : #include <xapian/error.h>
      28                 :            : 
      29                 :            : #include "omassert.h"
      30                 :            : 
      31                 :            : #include "serialise-double.h"
      32                 :            : 
      33                 :            : #include <cfloat>
      34                 :            : #include <cmath>
      35                 :            : 
      36                 :            : #include <algorithm>
      37                 :            : #include <string>
      38                 :            : 
      39                 :            : using namespace std;
      40                 :            : 
      41                 :            : // The serialisation we use for doubles is inspired by a comp.lang.c post
      42                 :            : // by Jens Moeller:
      43                 :            : //
      44                 :            : // http://groups.google.com/group/comp.lang.c/browse_thread/thread/6558d4653f6dea8b/75a529ec03148c98
      45                 :            : //
      46                 :            : // The clever part is that the mantissa is encoded as a base-256 number which
      47                 :            : // means there's no rounding error provided both ends have FLT_RADIX as some
      48                 :            : // power of two.
      49                 :            : //
      50                 :            : // FLT_RADIX == 2 seems to be ubiquitous on modern UNIX platforms, while
      51                 :            : // some older platforms used FLT_RADIX == 16 (IBM machines for example).
      52                 :            : // FLT_RADIX == 10 seems to be very rare (the only instance Google finds
      53                 :            : // is for a cross-compiler to some TI calculators).
      54                 :            : 
      55                 :            : #if FLT_RADIX == 2
      56                 :            : # define MAX_MANTISSA_BYTES ((DBL_MANT_DIG + 7 + 7) / 8)
      57                 :            : # define MAX_EXP ((DBL_MAX_EXP + 1) / 8)
      58                 :            : # define MAX_MANTISSA (1 << (DBL_MAX_EXP & 7))
      59                 :            : #elif FLT_RADIX == 16
      60                 :            : # define MAX_MANTISSA_BYTES ((DBL_MANT_DIG + 1 + 1) / 2)
      61                 :            : # define MAX_EXP ((DBL_MAX_EXP + 1) / 2)
      62                 :            : # define MAX_MANTISSA (1 << ((DBL_MAX_EXP & 1) * 4))
      63                 :            : #else
      64                 :            : # error FLT_RADIX is a value not currently handled (not 2 or 16)
      65                 :            : // # define MAX_MANTISSA_BYTES (sizeof(double) + 1)
      66                 :            : #endif
      67                 :            : 
      68                 :      84592 : static int base256ify_double(double &v) {
      69                 :            :     int exp;
      70                 :      84592 :     v = frexp(v, &exp);
      71                 :            :     // v is now in the range [0.5, 1.0)
      72                 :      84592 :     --exp;
      73                 :      84592 :     v = ldexp(v, (exp & 7) + 1);
      74                 :            :     // v is now in the range [1.0, 256.0)
      75                 :      84592 :     exp >>= 3;
      76                 :      84592 :     return exp;
      77                 :            : }
      78                 :            : 
      79                 :      83773 : std::string serialise_double(double v)
      80                 :            : {
      81                 :            :     /* First byte:
      82                 :            :      *  bit 7 Negative flag
      83                 :            :      *  bit 4..6 Mantissa length - 1
      84                 :            :      *  bit 0..3 --- 0-13 -> Exponent + 7
      85                 :            :      *            \- 14 -> Exponent given by next byte
      86                 :            :      *             - 15 -> Exponent given by next 2 bytes
      87                 :            :      *
      88                 :            :      * Then optional medium (1 byte) or large exponent (2 bytes, lsb first)
      89                 :            :      *
      90                 :            :      * Then mantissa (0 iff value is 0)
      91                 :            :      */
      92                 :            : 
      93                 :      83773 :     bool negative = (v < 0.0);
      94                 :            : 
      95         [ +  + ]:      83773 :     if (negative) v = -v;
      96                 :            : 
      97                 :      83773 :     int exp = base256ify_double(v);
      98                 :            : 
      99                 :      83773 :     string result;
     100                 :            : 
     101   [ +  +  +  + ]:     167508 :     if (exp <= 6 && exp >= -7) {
     102                 :      83735 :         unsigned char b = static_cast<unsigned char>(exp + 7);
     103         [ +  + ]:      83735 :         if (negative) b |= static_cast<unsigned char>(0x80);
     104                 :      83735 :         result += char(b);
     105                 :            :     } else {
     106 [ +  - ][ +  + ]:         48 :         if (exp >= -128 && exp < 127) {
     107         [ +  + ]:         10 :             result += negative ? char(0x8e) : char(0x0e);
     108                 :         10 :             result += char(exp + 128);
     109                 :            :         } else {
     110 [ +  - ][ -  + ]:         28 :             if (exp < -32768 || exp > 32767) {
     111                 :          0 :                 throw Xapian::InternalError("Insane exponent in floating point number");
     112                 :            :             }
     113         [ +  + ]:         28 :             result += negative ? char(0x8f) : char(0x0f);
     114                 :         28 :             result += char(unsigned(exp + 32768) & 0xff);
     115                 :         28 :             result += char(unsigned(exp + 32768) >> 8);
     116                 :            :         }
     117                 :            :     }
     118                 :            : 
     119                 :      83773 :     int maxbytes = min(MAX_MANTISSA_BYTES, 8);
     120                 :            : 
     121                 :      83773 :     size_t n = result.size();
     122   [ +  +  +  - ]:     381868 :     do {
                 [ +  + ]
     123                 :     381868 :         unsigned char byte = static_cast<unsigned char>(v);
     124                 :     381868 :         result += char(byte);
     125                 :     381868 :         v -= double(byte);
     126                 :     381868 :         v *= 256.0;
     127                 :            :     } while (v != 0.0 && --maxbytes);
     128                 :            : 
     129                 :      83773 :     n = result.size() - n;
     130         [ +  + ]:      83773 :     if (n > 1) {
     131                 :            :         Assert(n <= 8);
     132                 :      83773 :         result[0] = static_cast<unsigned char>(result[0] | ((n - 1) << 4));
     133                 :            :     }
     134                 :            : 
     135                 :          0 :     return result;
     136                 :            : }
     137                 :            : 
     138                 :      87293 : double unserialise_double(const char ** p, const char *end)
     139                 :            : {
     140         [ -  + ]:      87293 :     if (end - *p < 2) {
     141                 :          0 :         throw Xapian::SerialisationError("Bad encoded double: insufficient data");
     142                 :            :     }
     143                 :      87293 :     unsigned char first = *(*p)++;
     144 [ -  + ][ #  # ]:      87293 :     if (first == 0 && *(*p) == 0) {
     145                 :          0 :         ++*p;
     146                 :          0 :         return 0.0;
     147                 :            :     }
     148                 :            : 
     149                 :      87293 :     bool negative = (first & 0x80) != 0;
     150                 :      87293 :     size_t mantissa_len = ((first >> 4) & 0x07) + 1;
     151                 :            : 
     152                 :      87293 :     int exp = first & 0x0f;
     153         [ +  + ]:      87293 :     if (exp >= 14) {
     154                 :         38 :         int bigexp = static_cast<unsigned char>(*(*p)++);
     155         [ +  + ]:         38 :         if (exp == 15) {
     156         [ -  + ]:         28 :             if (*p == end) {
     157                 :          0 :                 throw Xapian::SerialisationError("Bad encoded double: short large exponent");
     158                 :            :             }
     159                 :         28 :             exp = bigexp | (static_cast<unsigned char>(*(*p)++) << 8);
     160                 :         28 :             exp -= 32768;
     161                 :            :         } else {
     162                 :         10 :             exp = bigexp - 128;
     163                 :            :         }
     164                 :            :     } else {
     165                 :      87255 :         exp -= 7;
     166                 :            :     }
     167                 :            : 
     168         [ -  + ]:      87293 :     if (size_t(end - *p) < mantissa_len) {
     169                 :          0 :         throw Xapian::SerialisationError("Bad encoded double: short mantissa");
     170                 :            :     }
     171                 :            : 
     172                 :      87293 :     double v = 0.0;
     173                 :            : 
     174                 :            :     static double dbl_max_mantissa = DBL_MAX;
     175 [ +  + ][ +  - ]:      87293 :     static int dbl_max_exp = base256ify_double(dbl_max_mantissa);
     176                 :      87293 :     *p += mantissa_len;
     177 [ +  - ][ +  + ]:      87293 :     if (exp > dbl_max_exp ||
                 [ -  + ]
     178                 :            :         (exp == dbl_max_exp && double(**p) > dbl_max_mantissa)) {
     179                 :            :         // The mantissa check should be precise provided that FLT_RADIX
     180                 :            :         // is a power of 2.
     181                 :          0 :         v = HUGE_VAL;
     182                 :            :     } else {
     183                 :      87293 :         const char *q = *p;
     184         [ +  + ]:     475408 :         while (mantissa_len--) {
     185                 :     388115 :             v *= 0.00390625; // 1/256
     186                 :     388115 :             v += double(static_cast<unsigned char>(*--q));
     187                 :            :         }
     188                 :            : 
     189         [ +  + ]:      87293 :         if (exp) v = ldexp(v, exp * 8);
     190                 :            : 
     191                 :            : #if 0
     192                 :            :         if (v == 0.0) {
     193                 :            :             // FIXME: handle underflow
     194                 :            :         }
     195                 :            : #endif
     196                 :            :     }
     197                 :            : 
     198         [ +  + ]:      87293 :     if (negative) v = -v;
     199                 :            : 
     200                 :      87293 :     return v;
     201                 :            : }

Generated by: LCOV version 1.8