xapian-core  2.0.0
serialise-double.cc
Go to the documentation of this file.
1 
4 /* Copyright (C) 2006,2007,2008,2009,2015,2025 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 "wordaccess.h"
34 
35 #include <cfloat>
36 #include <cmath>
37 
38 #include <algorithm>
39 #include <limits>
40 #include <string>
41 
42 using namespace std;
43 
44 #ifdef FOLLOWS_IEEE
45 
46 string serialise_double(double v)
47 {
48 # ifdef WORDS_BIGENDIAN
49  uint64_t temp;
50  static_assert(sizeof(temp) == sizeof(v),
51  "Check if size of double and 64 bit int is same");
52  memcpy(&temp, &v, sizeof(double));
53  temp = do_bswap(temp);
54  return string(reinterpret_cast<const char *>(&temp), sizeof(double));
55 # else
56  return string(reinterpret_cast<const char *>(&v), sizeof(double));
57 # endif
58 }
59 
60 double unserialise_double(const char ** p, const char * end)
61 {
62  if (end - *p < 8) {
64  "Bad encoded double: insufficient data");
65  }
66  double result;
67 # ifdef WORDS_BIGENDIAN
68  uint64_t temp;
69  static_assert(sizeof(temp) == sizeof(double),
70  "Check if size of double and 64 bit int is same");
71  memcpy(&temp, *p, sizeof(double));
72  temp = do_bswap(temp);
73  memcpy(&result, &temp, sizeof(double));
74 # else
75  memcpy(&result, *p, sizeof(double));
76 # endif
77  *p += 8;
78  return result;
79 }
80 
81 #else
82 
83 string serialise_double(double v)
84 {
85  /* First bit(msb) -> sign (1 means negative)
86  * next 11 bits -> exponent
87  * last 52 bits -> mantissa
88  *
89  * frexp gives fraction within the range [0.5, 1)
90  * We multiply it by 2 to change the range to [1.0, 2.0)
91  * and reduce exp by 1, since this is the way doubles
92  * are stored in IEEE-754.
93  *
94  * Conversion of mantissa to bits is done by
95  * multiplying the mantissa with 2^52, converting
96  * it to a 64 bit integer representation of the original
97  * double.
98  */
99 
100  static_assert(uint64_t(1) << 52 < numeric_limits<double>::max(),
101  "Check if 2^52 can be represented by a double");
102 
103  uint64_t result = 0;
104 
105  if (v == 0.0) {
106  result = 0;
107  return string(reinterpret_cast<const char *>(&result),
108  sizeof(uint64_t));
109  }
110 
111  if (rare(!isfinite(v))) {
112  // frexp() returns an unspecified exponent for infinities and NaN so
113  // we need to special case these.
114  const static char pos_inf[] = {
115  '\x7f', '\xf0', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'
116  };
117  const static char neg_inf[] = {
118  '\xff', '\xf0', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'
119  };
120  const static char pos_nan[] = {
121  '\x7f', '\xf8', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'
122  };
123  const static char neg_nan[] = {
124  '\xff', '\xf8', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'
125  };
126  if (isinf(v)) {
127  return string(v > 0 ? pos_inf : neg_inf, 8);
128  }
129  return string(v > 0 ? pos_nan : neg_nan, 8);
130  }
131 
132  bool negative = (v < 0.0);
133  if (negative) {
134  v = -v;
135  result |= uint64_t(1) << 63;
136  }
137 
138  int exp;
139  v = frexp(v, &exp);
140  v *= 2.0;
141  v -= 1.0;
142  exp += 1022;
143 
144  result |= uint64_t(exp) << 52;
145 
146 # if FLT_RADIX == 2
147  double scaled_v = scalbn(v, 52);
148 # else
149  double scaled_v = ldexp(v, 52);
150 # endif
151 
152  uint64_t scaled_v_int = static_cast<uint64_t>(scaled_v);
153  result |= scaled_v_int;
154 
155 # ifdef WORDS_BIGENDIAN
156  result = do_bswap(result);
157 # endif
158 
159  return string(reinterpret_cast<const char *>(&result), sizeof(uint64_t));
160 }
161 
162 double unserialise_double(const char ** p, const char * end) {
163  if (end - *p < 8) {
165  "Bad encoded double: insufficient data");
166  }
167  unsigned char first = *(*p + 7); // little-endian stored
168  unsigned char second = *(*p + 6);
169 
170  bool negative = (first & (0x80)) != 0;
171 
172  // bitwise operations to extract exponent
173  int exp = (first & (0x80 - 1));
174  exp <<= 4;
175  exp |= (second & (15 << 4)) >> 4;
176  exp -= 1023;
177 
178  uint64_t mantissa_bp; // variable to store bit pattern of mantissa;
179  memcpy(&mantissa_bp, *p, sizeof(double));
180  mantissa_bp &= (uint64_t(1) << 52) - 1;
181 
182  *p += 8;
183 
184  if (exp + 1023 == 0 && mantissa_bp == 0) return 0.0;
185 
186  if (rare(exp == 1024)) {
187  // Infinity or NaN. The mantissa is non-zero for NaN.
188  if (mantissa_bp != 0) {
189  // If NaNs are not supported, nan() returns zero which seems as
190  // good a value as any to use.
191  return negative ? -nan("") : nan("");
192  }
193  // HUGE_VAL is infinity is the implementation support infinity,
194  // and otherwise is a very large value which is our best fallback.
195  return negative ? -HUGE_VAL : HUGE_VAL;
196  }
197 
198 # if FLT_RADIX == 2
199  double result = scalbn(mantissa_bp, -52);
200  result = scalbn(result + 1.0, exp);
201 # else
202  double result = ldexp(mantissa_bp, -52);
203  result = ldexp(result + 1.0, exp);
204 # endif
205 
206  if (negative) result = -result;
207  return result;
208 }
209 
210 #endif
Indicates an error in the std::string serialisation of an object.
Definition: error.h:917
#define rare(COND)
Definition: config.h:607
PositionList * p
Hierarchy of classes which Xapian can throw as exceptions.
Various assertion macros.
string serialise_double(double v)
Serialise a double to a string.
double unserialise_double(const char **p, const char *end)
Unserialise a double serialised by serialise_double.
functions to serialise and unserialise a double
functions for reading and writing different width words
uint16_t do_bswap(uint16_t value)
Definition: wordaccess.h:43