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_attributes.h>
16#include <daw/daw_bit_cast.h>
17#include <daw/daw_cxmath.h>
18#include <daw/daw_is_constant_evaluated.h>
19
20#include <cstddef>
21#include <cstdint>
22#include <limits>
23#include <type_traits>
24
25#if defined( DAW_HAS_MSVC ) and defined( _M_X64 )
26#include <intrin.h>
27#endif
28
29namespace daw::json {
30 inline namespace DAW_JSON_VER {
31 namespace json_details {
32 namespace eisellemire_details {
33 struct uint128 {
34 std::uint64_t low;
35 std::uint64_t high;
36 };
37
38 [[nodiscard]] constexpr bool
39 try_append_digit( std::uint64_t &value, unsigned digit ) noexcept {
40 constexpr auto max_value = std::numeric_limits<std::uint64_t>::max( );
41 if( digit > 9U or value > ( max_value - digit ) / 10U ) {
42 return false;
43 }
44 value = value * 10U + digit;
45 return true;
46 }
47
48 template<typename Real>
49 struct binary_format;
50
51 template<>
52 struct binary_format<double> {
53 using uint_type = std::uint64_t;
54 static constexpr std::int32_t mantissa_bits = 52;
55 static constexpr std::int32_t exponent_bias = 1023;
56 static constexpr std::int32_t infinite_power = 0x7FF;
57 static constexpr std::int32_t sign_bit = 63;
58 static constexpr std::int32_t smallest_power_of_ten = -342;
59 static constexpr std::int32_t largest_power_of_ten = 308;
60 static constexpr std::int32_t min_round_to_even = -4;
61 static constexpr std::int32_t max_round_to_even = 23;
62 };
63
64 template<>
65 struct binary_format<float> {
66 using uint_type = std::uint32_t;
67 static constexpr std::int32_t mantissa_bits = 23;
68 static constexpr std::int32_t exponent_bias = 127;
69 static constexpr std::int32_t infinite_power = 0xFF;
70 static constexpr std::int32_t sign_bit = 31;
71 static constexpr std::int32_t smallest_power_of_ten = -64;
72 static constexpr std::int32_t largest_power_of_ten = 38;
73 static constexpr std::int32_t min_round_to_even = -17;
74 static constexpr std::int32_t max_round_to_even = 10;
75 };
76
77 [[nodiscard]] constexpr uint128
78 full_multiplication_generic( std::uint64_t lhs,
79 std::uint64_t rhs ) noexcept {
80 auto const lhs_hi = lhs >> 32U;
81 auto const lhs_lo = lhs & 0xFFFF'FFFFULL;
82 auto const rhs_hi = rhs >> 32U;
83 auto const rhs_lo = rhs & 0xFFFF'FFFFULL;
84
85 auto const lhs_hi_rhs_lo = lhs_hi * rhs_lo;
86 auto const lhs_lo_rhs_lo = lhs_lo * rhs_lo;
87 auto const middle = lhs_hi_rhs_lo + lhs_lo * rhs_hi;
88 auto const middle_carry = middle < lhs_hi_rhs_lo;
89 auto const low = lhs_lo_rhs_lo + ( middle << 32U );
90 auto const high =
91 lhs_hi * rhs_hi + ( middle >> 32U ) +
92 ( static_cast<std::uint64_t>( middle_carry ) << 32U ) +
93 static_cast<std::uint64_t>( low < lhs_lo_rhs_lo );
94 return { low, high };
95 }
96
97 [[nodiscard]] DAW_ATTRIB_FLATINLINE constexpr uint128
98 full_multiplication( std::uint64_t lhs, std::uint64_t rhs ) noexcept {
99#if ( defined( __GNUC__ ) or defined( __clang__ ) ) and \
100 defined( __SIZEOF_INT128__ )
101 if( not DAW_IS_CONSTANT_EVALUATED_COMPAT( ) ) {
102#if defined( __GNUC__ )
103#pragma GCC diagnostic push
104#pragma GCC diagnostic ignored "-Wpedantic"
105#endif
106 auto const result = static_cast<unsigned __int128>( lhs ) * rhs;
107 return { static_cast<std::uint64_t>( result ),
108 static_cast<std::uint64_t>( result >> 64U ) };
109#if defined( __GNUC__ )
110#pragma GCC diagnostic pop
111#endif
112 }
113#elif defined( DAW_HAS_MSVC ) and defined( _M_X64 )
114 if( not DAW_IS_CONSTANT_EVALUATED_COMPAT( ) ) {
115 uint128 result{ };
116 result.low = _umul128( lhs, rhs, &result.high );
117 return result;
118 }
119#endif
120 return full_multiplication_generic( lhs, rhs );
121 }
122
123 template<typename Real>
124 [[nodiscard]] DAW_ATTRIB_FLATINLINE constexpr uint128
125 compute_product( std::int64_t exponent,
126 std::uint64_t significant_digits ) noexcept {
127 auto const index = static_cast<std::size_t>(
128 2 * ( exponent - ( -342 /* smallest cached power */ ) ) );
129 auto product =
130 full_multiplication( significant_digits, pow5_tbl[index] );
131
132 // Three extra bits are required: the implicit bit, the rounding bit,
133 // and one bit that may be lost while normalizing the product.
134 constexpr std::uint64_t precision_mask =
135 std::numeric_limits<std::uint64_t>::max( ) >>
136 ( binary_format<Real>::mantissa_bits + 3 );
137 if( ( product.high & precision_mask ) == precision_mask ) {
138 auto const second =
139 full_multiplication( significant_digits, pow5_tbl[index + 1] );
140 product.low += second.high;
141 product.high +=
142 static_cast<std::uint64_t>( second.high > product.low );
143 }
144 return product;
145 }
146
147 [[nodiscard]] constexpr std::int32_t
148 binary_power( std::int32_t exponent ) noexcept {
149 // floor(log2(10^exponent)) plus the cached-power normalization.
150 return ( ( 217706 * exponent ) >> 16 ) + 63;
151 }
152 } // namespace eisellemire_details
153
164 template<typename Real = double>
165 [[nodiscard]] DAW_ATTRIB_FLATINLINE constexpr Real
166 parse_real_eisellemire( bool negative, std::int64_t exponent,
167 std::uint64_t significant_digits ) noexcept {
168 static_assert( std::is_same_v<Real, float> or
169 std::is_same_v<Real, double>,
170 "parse_real_lemire supports float and double" );
171 static_assert( std::numeric_limits<Real>::is_iec559 );
172 using format = eisellemire_details::binary_format<Real>;
173 using uint_type = typename format::uint_type;
174
175 std::uint64_t mantissa = 0;
176 std::int32_t power2 = 0;
177 if( significant_digits == 0 or
178 exponent < format::smallest_power_of_ten ) {
179 // The sign is applied while packing so underflow preserves -0.0.
180 } else if( exponent > format::largest_power_of_ten ) {
181 power2 = format::infinite_power;
182 } else {
183 auto const leading_zeroes = static_cast<std::int32_t>(
184 daw::cxmath::count_leading_zeroes( significant_digits ) );
185 auto const normalized_digits = significant_digits << leading_zeroes;
186 auto const product = eisellemire_details::compute_product<Real>(
187 exponent, normalized_digits );
188 auto const upper_bit =
189 static_cast<std::int32_t>( product.high >> 63U );
190 auto const shift = upper_bit + 64 - format::mantissa_bits - 3;
191
192 mantissa = product.high >> shift;
193 power2 = eisellemire_details::binary_power(
194 static_cast<std::int32_t>( exponent ) ) +
195 upper_bit - leading_zeroes + format::exponent_bias;
196
197 if( power2 <= 0 ) {
198 if( -power2 + 1 >= 64 ) {
199 mantissa = 0;
200 power2 = 0;
201 } else {
202 mantissa >>= -power2 + 1;
203 mantissa += mantissa & 1U;
204 mantissa >>= 1U;
205 power2 =
206 mantissa < ( std::uint64_t{ 1 } << format::mantissa_bits ) ? 0
207 : 1;
208 }
209 } else {
210 // Correct exact halfway cases to round-to-even before the usual
211 // round-up operation.
212 if( product.low <= 1 and exponent >= format::min_round_to_even and
213 exponent <= format::max_round_to_even and
214 ( mantissa & 3U ) == 1U and
215 ( mantissa << shift ) == product.high ) {
216 mantissa &= ~std::uint64_t{ 1 };
217 }
218 mantissa += mantissa & 1U;
219 mantissa >>= 1U;
220 if( mantissa >= ( std::uint64_t{ 2 } << format::mantissa_bits ) ) {
221 mantissa = std::uint64_t{ 1 } << format::mantissa_bits;
222 ++power2;
223 }
224 mantissa &= ~( std::uint64_t{ 1 } << format::mantissa_bits );
225 if( power2 >= format::infinite_power ) {
226 mantissa = 0;
227 power2 = format::infinite_power;
228 }
229 }
230 }
231
232 auto const bits = static_cast<uint_type>(
233 mantissa |
234 ( static_cast<std::uint64_t>( power2 ) << format::mantissa_bits ) |
235 ( static_cast<std::uint64_t>( negative ) << format::sign_bit ) );
236 return DAW_BIT_CAST( Real, bits );
237 }
238 } // namespace json_details
239 } // namespace DAW_JSON_VER
240} // 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