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
50 template<typename Real>
51 struct binary_format;
52
53 template<>
54 struct binary_format<double> {
55 using uint_type = std::uint64_t;
56 static constexpr std::int32_t mantissa_bits = 52;
57 static constexpr std::int32_t exponent_bias = 1023;
58 static constexpr std::int32_t infinite_power = 0x7FF;
59 static constexpr std::int32_t sign_bit = 63;
60 static constexpr std::int32_t smallest_power_of_ten = -342;
61 static constexpr std::int32_t largest_power_of_ten = 308;
62 static constexpr std::int32_t min_round_to_even = -4;
63 static constexpr std::int32_t max_round_to_even = 23;
64 };
65
66 template<>
67 struct binary_format<float> {
68 using uint_type = std::uint32_t;
69 static constexpr std::int32_t mantissa_bits = 23;
70 static constexpr std::int32_t exponent_bias = 127;
71 static constexpr std::int32_t infinite_power = 0xFF;
72 static constexpr std::int32_t sign_bit = 31;
73 static constexpr std::int32_t smallest_power_of_ten = -64;
74 static constexpr std::int32_t largest_power_of_ten = 38;
75 static constexpr std::int32_t min_round_to_even = -17;
76 static constexpr std::int32_t max_round_to_even = 10;
77 };
78
79 [[nodiscard]] constexpr uint128
80 full_multiplication_generic( std::uint64_t lhs,
81 std::uint64_t rhs ) noexcept {
82 auto const lhs_hi = lhs >> 32U;
83 auto const lhs_lo = lhs & 0xFFFF'FFFFULL;
84 auto const rhs_hi = rhs >> 32U;
85 auto const rhs_lo = rhs & 0xFFFF'FFFFULL;
86
87 auto const lhs_hi_rhs_lo = lhs_hi * rhs_lo;
88 auto const lhs_lo_rhs_lo = lhs_lo * rhs_lo;
89 auto const middle = lhs_hi_rhs_lo + lhs_lo * rhs_hi;
90 auto const middle_carry = middle < lhs_hi_rhs_lo;
91 auto const low = lhs_lo_rhs_lo + ( middle << 32U );
92 auto const high =
93 lhs_hi * rhs_hi + ( middle >> 32U ) +
94 ( static_cast<std::uint64_t>( middle_carry ) << 32U ) +
95 static_cast<std::uint64_t>( low < lhs_lo_rhs_lo );
96 return { low, high };
97 }
98
99 [[nodiscard]] DAW_ATTRIB_FLATINLINE constexpr uint128
100 full_multiplication( std::uint64_t lhs, std::uint64_t rhs ) noexcept {
101#if ( defined( __GNUC__ ) or defined( __clang__ ) ) and \
102 defined( __SIZEOF_INT128__ )
103 if( not DAW_IS_CONSTANT_EVALUATED_COMPAT( ) ) {
104#if defined( __GNUC__ )
105#pragma GCC diagnostic push
106#pragma GCC diagnostic ignored "-Wpedantic"
107#endif
108 auto const result = static_cast<unsigned __int128>( lhs ) * rhs;
109 return { static_cast<std::uint64_t>( result ),
110 static_cast<std::uint64_t>( result >> 64U ) };
111#if defined( __GNUC__ )
112#pragma GCC diagnostic pop
113#endif
114 }
115#elif defined( DAW_HAS_MSVC ) and defined( _M_X64 )
116 if( not DAW_IS_CONSTANT_EVALUATED_COMPAT( ) ) {
117 uint128 result{ };
118 result.low = _umul128( lhs, rhs, &result.high );
119 return result;
120 }
121#endif
122 return full_multiplication_generic( lhs, rhs );
123 }
124
125 template<typename Real>
126 [[nodiscard]] DAW_ATTRIB_FLATINLINE constexpr uint128
127 compute_product( std::int64_t exponent,
128 std::uint64_t significant_digits ) noexcept {
129 auto const index = static_cast<std::size_t>(
130 2 * ( exponent - ( -342 /* smallest cached power */ ) ) );
131 auto product =
132 full_multiplication( significant_digits, pow5_tbl[index] );
133
134 // Three extra bits are required: the implicit bit, the rounding bit,
135 // and one bit that may be lost while normalizing the product.
136 DAW_CPP23_STATIC_LOCAL constexpr std::uint64_t precision_mask =
137 daw::max_value<std::uint64_t> >>
138 ( binary_format<Real>::mantissa_bits + 3 );
139 if( ( product.high & precision_mask ) == precision_mask ) {
140 auto const second =
141 full_multiplication( significant_digits, pow5_tbl[index + 1] );
142 product.low += second.high;
143 product.high +=
144 static_cast<std::uint64_t>( second.high > product.low );
145 }
146 return product;
147 }
148
149 [[nodiscard]] constexpr std::int32_t
150 binary_power( std::int32_t exponent ) noexcept {
151 // floor(log2(10^exponent)) plus the cached-power normalization.
152 return ( ( 217706 * exponent ) >> 16 ) + 63;
153 }
154 } // namespace eisellemire_details
155
159 template<typename Real>
160 inline constexpr bool is_double_sized_long_double_v =
161 std::is_same_v<Real, long double> and
162 sizeof( long double ) == sizeof( double ) and
163 daw::digits<long double> ==
164 daw::digits<double> and
165 std::numeric_limits<long double>::max_exponent ==
166 std::numeric_limits<double>::max_exponent;
167
178 template<typename Real = double>
179 [[nodiscard]] DAW_ATTRIB_FLATINLINE constexpr Real
180 parse_real_eisellemire( bool negative, std::int64_t exponent,
181 std::uint64_t significant_digits ) noexcept {
182 static_assert( std::is_same_v<Real, float> or
183 std::is_same_v<Real, double>,
184 "parse_real_lemire supports float and double" );
185 static_assert( std::numeric_limits<Real>::is_iec559 );
186 using format = eisellemire_details::binary_format<Real>;
187 using uint_type = typename format::uint_type;
188
189 std::uint64_t mantissa = 0;
190 std::int32_t power2 = 0;
191 if( significant_digits == 0 or
192 exponent < format::smallest_power_of_ten ) {
193 // The sign is applied while packing so underflow preserves -0.0.
194 } else if( exponent > format::largest_power_of_ten ) {
195 power2 = format::infinite_power;
196 } else {
197 auto const leading_zeroes = static_cast<std::int32_t>(
198 daw::cxmath::count_leading_zeroes( significant_digits ) );
199 auto const normalized_digits = significant_digits << leading_zeroes;
200 auto const product = eisellemire_details::compute_product<Real>(
201 exponent, normalized_digits );
202 auto const upper_bit =
203 static_cast<std::int32_t>( product.high >> 63U );
204 auto const shift = upper_bit + 64 - format::mantissa_bits - 3;
205
206 mantissa = product.high >> shift;
207 power2 = eisellemire_details::binary_power(
208 static_cast<std::int32_t>( exponent ) ) +
209 upper_bit - leading_zeroes + format::exponent_bias;
210
211 if( power2 <= 0 ) {
212 if( -power2 + 1 >= 64 ) {
213 mantissa = 0;
214 power2 = 0;
215 } else {
216 mantissa >>= -power2 + 1;
217 mantissa += mantissa & 1U;
218 mantissa >>= 1U;
219 power2 =
220 mantissa < ( std::uint64_t{ 1 } << format::mantissa_bits ) ? 0
221 : 1;
222 }
223 } else {
224 // Correct exact halfway cases to round-to-even before the usual
225 // round-up operation.
226 if( product.low <= 1 and exponent >= format::min_round_to_even and
227 exponent <= format::max_round_to_even and
228 ( mantissa & 3U ) == 1U and
229 ( mantissa << shift ) == product.high ) {
230 mantissa &= ~std::uint64_t{ 1 };
231 }
232 mantissa += mantissa & 1U;
233 mantissa >>= 1U;
234 if( mantissa >= ( std::uint64_t{ 2 } << format::mantissa_bits ) ) {
235 mantissa = std::uint64_t{ 1 } << format::mantissa_bits;
236 ++power2;
237 }
238 mantissa &= ~( std::uint64_t{ 1 } << format::mantissa_bits );
239 if( power2 >= format::infinite_power ) {
240 mantissa = 0;
241 power2 = format::infinite_power;
242 }
243 }
244 }
245
246 auto const bits = static_cast<uint_type>(
247 mantissa |
248 ( static_cast<std::uint64_t>( power2 ) << format::mantissa_bits ) |
249 ( static_cast<std::uint64_t>( negative ) << format::sign_bit ) );
250 return DAW_BIT_CAST( Real, bits );
251 }
252 } // namespace json_details
253 } // namespace DAW_JSON_VER
254} // namespace daw::json
Customization point traits.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20