00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include <config.h>
00026
00027 #include <xapian/error.h>
00028
00029 #include "omassert.h"
00030
00031 #include "serialise-double.h"
00032
00033 #include <cfloat>
00034 #include <cmath>
00035
00036 #include <algorithm>
00037 #include <string>
00038
00039 using namespace std;
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055 #if FLT_RADIX == 2
00056 # define MAX_MANTISSA_BYTES ((DBL_MANT_DIG + 7 + 7) / 8)
00057 # define MAX_EXP ((DBL_MAX_EXP + 1) / 8)
00058 # define MAX_MANTISSA (1 << (DBL_MAX_EXP & 7))
00059 #elif FLT_RADIX == 16
00060 # define MAX_MANTISSA_BYTES ((DBL_MANT_DIG + 1 + 1) / 2)
00061 # define MAX_EXP ((DBL_MAX_EXP + 1) / 2)
00062 # define MAX_MANTISSA (1 << ((DBL_MAX_EXP & 1) * 4))
00063 #else
00064 # error FLT_RADIX is a value not currently handled (not 2 or 16)
00065
00066 #endif
00067
00068 static int base256ify_double(double &v) {
00069 int exp;
00070 v = frexp(v, &exp);
00071
00072 --exp;
00073 v = ldexp(v, (exp & 7) + 1);
00074
00075 exp >>= 3;
00076 return exp;
00077 }
00078
00079 std::string serialise_double(double v)
00080 {
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093 bool negative = (v < 0.0);
00094
00095 if (negative) v = -v;
00096
00097 int exp = base256ify_double(v);
00098
00099 string result;
00100
00101 if (exp <= 6 && exp >= -7) {
00102 unsigned char b = static_cast<unsigned char>(exp + 7);
00103 if (negative) b |= static_cast<unsigned char>(0x80);
00104 result += char(b);
00105 } else {
00106 if (exp >= -128 && exp < 127) {
00107 result += negative ? char(0x8e) : char(0x0e);
00108 result += char(exp + 128);
00109 } else {
00110 if (exp < -32768 || exp > 32767) {
00111 throw Xapian::InternalError("Insane exponent in floating point number");
00112 }
00113 result += negative ? char(0x8f) : char(0x0f);
00114 result += char(unsigned(exp + 32768) & 0xff);
00115 result += char(unsigned(exp + 32768) >> 8);
00116 }
00117 }
00118
00119 int maxbytes = min(MAX_MANTISSA_BYTES, 8);
00120
00121 size_t n = result.size();
00122 do {
00123 unsigned char byte = static_cast<unsigned char>(v);
00124 result += char(byte);
00125 v -= double(byte);
00126 v *= 256.0;
00127 } while (v != 0.0 && --maxbytes);
00128
00129 n = result.size() - n;
00130 if (n > 1) {
00131 Assert(n <= 8);
00132 result[0] = static_cast<unsigned char>(result[0] | ((n - 1) << 4));
00133 }
00134
00135 return result;
00136 }
00137
00138 double unserialise_double(const char ** p, const char *end)
00139 {
00140 if (end - *p < 2) {
00141 throw Xapian::SerialisationError("Bad encoded double: insufficient data");
00142 }
00143 unsigned char first = *(*p)++;
00144 if (first == 0 && *(*p) == 0) {
00145 ++*p;
00146 return 0.0;
00147 }
00148
00149 bool negative = (first & 0x80) != 0;
00150 size_t mantissa_len = ((first >> 4) & 0x07) + 1;
00151
00152 int exp = first & 0x0f;
00153 if (exp >= 14) {
00154 int bigexp = static_cast<unsigned char>(*(*p)++);
00155 if (exp == 15) {
00156 if (*p == end) {
00157 throw Xapian::SerialisationError("Bad encoded double: short large exponent");
00158 }
00159 exp = bigexp | (static_cast<unsigned char>(*(*p)++) << 8);
00160 exp -= 32768;
00161 } else {
00162 exp = bigexp - 128;
00163 }
00164 } else {
00165 exp -= 7;
00166 }
00167
00168 if (size_t(end - *p) < mantissa_len) {
00169 throw Xapian::SerialisationError("Bad encoded double: short mantissa");
00170 }
00171
00172 double v = 0.0;
00173
00174 static double dbl_max_mantissa = DBL_MAX;
00175 static int dbl_max_exp = base256ify_double(dbl_max_mantissa);
00176 *p += mantissa_len;
00177 if (exp > dbl_max_exp ||
00178 (exp == dbl_max_exp && double(**p) > dbl_max_mantissa)) {
00179
00180
00181 v = HUGE_VAL;
00182 } else {
00183 const char *q = *p;
00184 while (mantissa_len--) {
00185 v *= 0.00390625;
00186 v += double(static_cast<unsigned char>(*--q));
00187 }
00188
00189 if (exp) v = ldexp(v, exp * 8);
00190
00191 #if 0
00192 if (v == 0.0) {
00193
00194 }
00195 #endif
00196 }
00197
00198 if (negative) v = -v;
00199
00200 return v;
00201 }