[ci] Update macos jobs
[xapian.git] / xapian-core / common / serialise-double.cc
blobc4f1af894a13f433f4d819f489eaf217f07651a1
1 /** @file
2 * @brief functions to serialise and unserialise a double
3 */
4 /* Copyright (C) 2006,2007,2008,2009,2015 Olly Betts
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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.
25 #include <config.h>
27 #include <xapian/error.h>
29 #include "omassert.h"
31 #include "serialise-double.h"
33 #include "wordaccess.h"
35 #include <cfloat>
36 #include <cmath>
38 #include <algorithm>
39 #include <limits>
40 #include <string>
42 using namespace std;
44 #ifdef FOLLOWS_IEEE
46 string serialise_double(double v)
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
60 double unserialise_double(const char ** p, const char * end)
62 if (end - *p < 8) {
63 throw Xapian::SerialisationError(
64 "Bad encoded double: insufficient data");
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;
81 #else
83 string serialise_double(double v)
85 /* First bit(msb) -> sign (1 means negative)
86 * next 11 bits -> exponent
87 * last 52 bits -> mantissa
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.
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.
100 static_assert(uint64_t(1) << 52 < numeric_limits<double>::max(),
101 "Check if 2^52 can be represented by a double");
103 uint64_t result = 0;
105 if (v == 0.0) {
106 result = 0;
107 return string(reinterpret_cast<const char *>(&result),
108 sizeof(uint64_t));
111 bool negative = (v < 0.0);
112 if (negative) {
113 v = -v;
114 result |= uint64_t(1) << 63;
117 int exp;
118 v = frexp(v, &exp);
119 v *= 2.0;
120 v -= 1.0;
121 exp += 1022;
123 result |= uint64_t(exp) << 52;
125 # if FLT_RADIX == 2
126 double scaled_v = scalbn(v, 52);
127 # else
128 double scaled_v = ldexp(v, 52);
129 # endif
131 uint64_t scaled_v_int = static_cast<uint64_t>(scaled_v);
132 result |= scaled_v_int;
134 # ifdef WORDS_BIGENDIAN
135 result = do_bswap(result);
136 # endif
138 return string(reinterpret_cast<const char *>(&result), sizeof(uint64_t));
141 double unserialise_double(const char ** p, const char * end) {
142 if (end - *p < 8) {
143 throw Xapian::SerialisationError(
144 "Bad encoded double: insufficient data");
146 unsigned char first = *(*p + 7); // little-endian stored
147 unsigned char second = *(*p + 6);
149 bool negative = (first & (0x80)) != 0;
151 // bitwise operations to extract exponent
152 int exp = (first & (0x80 - 1));
153 exp <<= 4;
154 exp |= (second & (15 << 4)) >> 4;
155 exp -= 1023;
157 uint64_t mantissa_bp; // variable to store bit pattern of mantissa;
158 memcpy(&mantissa_bp, *p, sizeof(double));
159 mantissa_bp &= (uint64_t(1) << 52) - 1;
161 *p += 8;
163 if (exp + 1023 == 0 && mantissa_bp == 0) return 0.0;
165 # if FLT_RADIX == 2
166 double result = scalbn(mantissa_bp, -52);
167 result = scalbn(result + 1.0, exp);
168 # else
169 double result = ldexp(mantissa_bp, -52);
170 result = ldexp(result + 1.0, exp);
171 # endif
173 if (negative) result = -result;
174 return result;
177 #endif