DAW JSON Link
Loading...
Searching...
No Matches
daw_json_parse_real_eisellemire.h
Go to the documentation of this file.
1// Copyright (c) Darrell Wright
2//
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
5//
6// Official repository: https://github.com/beached/daw_json_link
7//
8
9#pragma once
10
12
14
15#include <daw/daw_arith_traits.h>
16#include <daw/daw_attributes.h>
17#include <daw/daw_bit_cast.h>
18#include <daw/daw_cxmath.h>
19#include <daw/daw_is_constant_evaluated.h>
20
21#include <cstddef>
22#include <cstdint>
23#include <limits>
24#include <type_traits>
25
26#if defined( DAW_HAS_MSVC ) and defined( _M_X64 )
27#include <intrin.h>
28#endif
29
30namespace daw::json {
31 inline namespace DAW_JSON_VER {
32 namespace json_details {
33 namespace eisellemire_details {
34 struct uint128 {
35 std::uint64_t low;
36 std::uint64_t high;
37 };
38
39 [[nodiscard]] constexpr bool
40 try_append_digit( std::uint64_t &value, unsigned digit ) noexcept {
41 DAW_CPP23_STATIC_LOCAL constexpr auto max_value =
42 daw::max_value<std::uint64_t>;
43 if( digit > 9U or value > ( max_value - digit ) / 10U ) {
44 return false;
45 }
46 value = value * 10U + digit;
47 return true;
48 }
49
53 template<typename Real>
54 struct binary_format {
55 static_assert( std::numeric_limits<Real>::is_iec559 and
56 std::numeric_limits<Real>::radix == 2,
57 "Only IEEE754 binary formats are supported" );
58 using uint_type = std::conditional_t<
59 sizeof( Real ) == 2, std::uint16_t,
60 std::conditional_t<sizeof( Real ) == 4, std::uint32_t,
61 std::uint64_t>>;
62 static_assert( sizeof( Real ) == sizeof( uint_type ),
63 "Formats wider than 64 bits are not supported" );
64 static constexpr std::int32_t mantissa_bits =
65 std::numeric_limits<Real>::digits - 1;
66 static constexpr std::int32_t exponent_bias =
67 std::numeric_limits<Real>::max_exponent - 1;
68 static constexpr std::int32_t infinite_power =
69 2 * std::numeric_limits<Real>::max_exponent - 1;
70 static constexpr std::int32_t sign_bit =
71 static_cast<std::int32_t>( sizeof( Real ) * 8U - 1U );
72 };
73
74 template<>
75 struct binary_format<double> {
76 using uint_type = std::uint64_t;
77 static constexpr std::int32_t mantissa_bits = 52;
78 static constexpr std::int32_t exponent_bias = 1023;
79 static constexpr std::int32_t infinite_power = 0x7FF;
80 static constexpr std::int32_t sign_bit = 63;
81 static constexpr std::int32_t smallest_power_of_ten = -342;
82 static constexpr std::int32_t largest_power_of_ten = 308;
83 static constexpr std::int32_t min_round_to_even = -4;
84 static constexpr std::int32_t max_round_to_even = 23;
85 };
86
87 template<>
88 struct binary_format<float> {
89 using uint_type = std::uint32_t;
90 static constexpr std::int32_t mantissa_bits = 23;
91 static constexpr std::int32_t exponent_bias = 127;
92 static constexpr std::int32_t infinite_power = 0xFF;
93 static constexpr std::int32_t sign_bit = 31;
94 static constexpr std::int32_t smallest_power_of_ten = -64;
95 static constexpr std::int32_t largest_power_of_ten = 38;
96 static constexpr std::int32_t min_round_to_even = -17;
97 static constexpr std::int32_t max_round_to_even = 10;
98 };
99
100 [[nodiscard]] constexpr uint128
101 full_multiplication_generic( std::uint64_t lhs,
102 std::uint64_t rhs ) noexcept {
103 auto const lhs_hi = lhs >> 32U;
104 auto const lhs_lo = lhs & 0xFFFF'FFFFULL;
105 auto const rhs_hi = rhs >> 32U;
106 auto const rhs_lo = rhs & 0xFFFF'FFFFULL;
107
108 auto const lhs_hi_rhs_lo = lhs_hi * rhs_lo;
109 auto const lhs_lo_rhs_lo = lhs_lo * rhs_lo;
110 auto const middle = lhs_hi_rhs_lo + lhs_lo * rhs_hi;
111 auto const middle_carry = middle < lhs_hi_rhs_lo;
112 auto const low = lhs_lo_rhs_lo + ( middle << 32U );
113 auto const high =
114 lhs_hi * rhs_hi + ( middle >> 32U ) +
115 ( static_cast<std::uint64_t>( middle_carry ) << 32U ) +
116 static_cast<std::uint64_t>( low < lhs_lo_rhs_lo );
117 return { low, high };
118 }
119
120 [[nodiscard]] DAW_ATTRIB_FLATINLINE constexpr uint128
121 full_multiplication( std::uint64_t lhs, std::uint64_t rhs ) noexcept {
122#if ( defined( __GNUC__ ) or defined( __clang__ ) ) and \
123 defined( __SIZEOF_INT128__ )
124 if( not DAW_IS_CONSTANT_EVALUATED_COMPAT( ) ) {
125#if defined( __GNUC__ )
126#pragma GCC diagnostic push
127#pragma GCC diagnostic ignored "-Wpedantic"
128#endif
129 auto const result = static_cast<unsigned __int128>( lhs ) * rhs;
130 return { static_cast<std::uint64_t>( result ),
131 static_cast<std::uint64_t>( result >> 64U ) };
132#if defined( __GNUC__ )
133#pragma GCC diagnostic pop
134#endif
135 }
136#elif defined( DAW_HAS_MSVC ) and defined( _M_X64 )
137 if( not DAW_IS_CONSTANT_EVALUATED_COMPAT( ) ) {
138 uint128 result{ };
139 result.low = _umul128( lhs, rhs, &result.high );
140 return result;
141 }
142#endif
143 return full_multiplication_generic( lhs, rhs );
144 }
145
146 template<typename Real>
147 [[nodiscard]] DAW_ATTRIB_FLATINLINE constexpr uint128
148 compute_product( std::int64_t exponent,
149 std::uint64_t significant_digits ) noexcept {
150 auto const index = static_cast<std::size_t>(
151 2 * ( exponent - ( -342 /* smallest cached power */ ) ) );
152 auto product =
153 full_multiplication( significant_digits, pow5_tbl[index] );
154
155 // Three extra bits are required: the implicit bit, the rounding bit,
156 // and one bit that may be lost while normalizing the product.
157 DAW_CPP23_STATIC_LOCAL constexpr std::uint64_t precision_mask =
158 daw::max_value<std::uint64_t> >>
159 ( binary_format<Real>::mantissa_bits + 3 );
160 if( ( product.high & precision_mask ) == precision_mask ) {
161 auto const second =
162 full_multiplication( significant_digits, pow5_tbl[index + 1] );
163 product.low += second.high;
164 product.high +=
165 static_cast<std::uint64_t>( second.high > product.low );
166 }
167 return product;
168 }
169
170 [[nodiscard]] constexpr std::int32_t
171 binary_power( std::int32_t exponent ) noexcept {
172 // floor(log2(10^exponent)) plus the cached-power normalization.
173 return ( ( 217706 * exponent ) >> 16 ) + 63;
174 }
175 } // namespace eisellemire_details
176
180 template<typename Real>
181 inline constexpr bool is_double_sized_long_double_v =
182 std::is_same_v<Real, long double> and
183 sizeof( long double ) == sizeof( double ) and
184 daw::digits<long double> ==
185 daw::digits<double> and
186 std::numeric_limits<long double>::max_exponent ==
187 std::numeric_limits<double>::max_exponent;
188
189 namespace eisellemire_details {
190 template<typename Real, typename Format>
191 inline constexpr bool has_same_format_v =
192 std::numeric_limits<Real>::is_iec559 and
193 std::numeric_limits<Real>::radix == 2 and
194 std::numeric_limits<Real>::digits ==
195 std::numeric_limits<Format>::digits and
196 std::numeric_limits<Real>::max_exponent ==
197 std::numeric_limits<Format>::max_exponent and
198 std::numeric_limits<Real>::min_exponent ==
199 std::numeric_limits<Format>::min_exponent;
200
201 template<typename Real, bool = std::is_floating_point_v<Real>>
202 struct lemire_compute {
203 using type = void;
204 };
205
206 template<typename Real>
207 struct lemire_compute<Real, true> {
208 using type = std::conditional_t<
209 has_same_format_v<Real, float>, float,
210 std::conditional_t<has_same_format_v<Real, double>, double, void>>;
211 };
212 } // namespace eisellemire_details
213
218 template<typename Real>
219 using lemire_compute_t =
220 typename eisellemire_details::lemire_compute<Real>::type;
221
222 template<typename Real>
223 inline constexpr bool is_lemire_capable_v =
224 not std::is_void_v<lemire_compute_t<Real>>;
225
236 template<typename Real = double>
237 [[nodiscard]] DAW_ATTRIB_FLATINLINE constexpr Real
238 parse_real_eisellemire( bool negative, std::int64_t exponent,
239 std::uint64_t significant_digits ) noexcept {
240 static_assert( std::is_same_v<Real, float> or
241 std::is_same_v<Real, double>,
242 "parse_real_lemire supports float and double" );
243 static_assert( std::numeric_limits<Real>::is_iec559 );
244 using format = eisellemire_details::binary_format<Real>;
245 using uint_type = typename format::uint_type;
246
247 std::uint64_t mantissa = 0;
248 std::int32_t power2 = 0;
249 if( significant_digits == 0 or
250 exponent < format::smallest_power_of_ten ) {
251 // The sign is applied while packing so underflow preserves -0.0.
252 } else if( exponent > format::largest_power_of_ten ) {
253 power2 = format::infinite_power;
254 } else {
255 auto const leading_zeroes = static_cast<std::int32_t>(
256 daw::cxmath::count_leading_zeroes( significant_digits ) );
257 auto const normalized_digits = significant_digits << leading_zeroes;
258 auto const product = eisellemire_details::compute_product<Real>(
259 exponent, normalized_digits );
260 auto const upper_bit =
261 static_cast<std::int32_t>( product.high >> 63U );
262 auto const shift = upper_bit + 64 - format::mantissa_bits - 3;
263
264 mantissa = product.high >> shift;
265 power2 = eisellemire_details::binary_power(
266 static_cast<std::int32_t>( exponent ) ) +
267 upper_bit - leading_zeroes + format::exponent_bias;
268
269 if( power2 <= 0 ) {
270 if( -power2 + 1 >= 64 ) {
271 mantissa = 0;
272 power2 = 0;
273 } else {
274 mantissa >>= -power2 + 1;
275 mantissa += mantissa & 1U;
276 mantissa >>= 1U;
277 power2 =
278 mantissa < ( std::uint64_t{ 1 } << format::mantissa_bits ) ? 0
279 : 1;
280 }
281 } else {
282 // Correct exact halfway cases to round-to-even before the usual
283 // round-up operation.
284 if( product.low <= 1 and exponent >= format::min_round_to_even and
285 exponent <= format::max_round_to_even and
286 ( mantissa & 3U ) == 1U and
287 ( mantissa << shift ) == product.high ) {
288 mantissa &= ~std::uint64_t{ 1 };
289 }
290 mantissa += mantissa & 1U;
291 mantissa >>= 1U;
292 if( mantissa >= ( std::uint64_t{ 2 } << format::mantissa_bits ) ) {
293 mantissa = std::uint64_t{ 1 } << format::mantissa_bits;
294 ++power2;
295 }
296 mantissa &= ~( std::uint64_t{ 1 } << format::mantissa_bits );
297 if( power2 >= format::infinite_power ) {
298 mantissa = 0;
299 power2 = format::infinite_power;
300 }
301 }
302 }
303
304 auto const bits = static_cast<uint_type>(
305 mantissa |
306 ( static_cast<std::uint64_t>( power2 ) << format::mantissa_bits ) |
307 ( static_cast<std::uint64_t>( negative ) << format::sign_bit ) );
308 return DAW_BIT_CAST( Real, bits );
309 }
310 } // namespace json_details
311 } // namespace DAW_JSON_VER
312} // namespace daw::json
string_writer grows its string with resize_and_overwrite, leaving the new bytes uninitialized,...
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20