Skip to content

Commit e20c952

Browse files
authored
Merge pull request #320 from toughengineer/int_multiplication_by_power_of_10
Implemented multiplication of integer by a power of 10
2 parents e5612e9 + 0a23032 commit e20c952

File tree

6 files changed

+316
-38
lines changed

6 files changed

+316
-38
lines changed

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,33 @@ int main() {
377377
}
378378
```
379379

380+
## Multiplication of an integer by a power of 10
381+
An integer `W` can be multiplied by a power of ten `10^Q` and
382+
converted to `double` with correctly rounded value
383+
(in "round to nearest, tie to even" fashion) using
384+
`fast_float::integer_times_pow10()`, e.g.:
385+
```C++
386+
const uint64_t W = 12345678901234567;
387+
const int Q = 23;
388+
const double result = fast_float::integer_times_pow10(W, Q);
389+
std::cout.precision(17);
390+
std::cout << W << " * 10^" << Q << " = " << result << " ("
391+
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
392+
```
393+
outputs
394+
```
395+
12345678901234567 * 10^23 = 1.2345678901234567e+39 (==expected)
396+
```
397+
`fast_float::integer_times_pow10()` gives the same result as
398+
using `fast_float::from_chars()` when parsing the string `"WeQ"`
399+
(in this example `"12345678901234567e23"`),
400+
except `fast_float::integer_times_pow10()` does not report out-of-range errors, and
401+
underflows to zero or overflows to infinity when the resulting value is
402+
out of range.
403+
404+
Overloads of `fast_float::integer_times_pow10()` are provided for
405+
signed and unsigned integer types: `int64_t`, `uint64_t`, etc.
406+
380407

381408
## Users and Related Work
382409

include/fast_float/fast_float.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,26 @@ FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
4545
from_chars_advanced(UC const *first, UC const *last, T &value,
4646
parse_options_t<UC> options) noexcept;
4747

48+
/**
49+
* This function multiplies an integer number by a power of 10 and returns
50+
* the result as a double precision floating-point value that is correctly
51+
* rounded. The resulting floating-point value is the closest floating-point
52+
* value, using the "round to nearest, tie to even" convention for values that
53+
* would otherwise fall right in-between two values. That is, we provide exact
54+
* conversion according to the IEEE standard.
55+
*
56+
* On overflow infinity is returned, on underflow 0 is returned.
57+
*
58+
* The implementation does not throw and does not allocate memory (e.g., with
59+
* `new` or `malloc`).
60+
*/
61+
FASTFLOAT_CONSTEXPR20 inline double
62+
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept;
63+
FASTFLOAT_CONSTEXPR20 inline
64+
typename std::enable_if<is_supported_float_type<double>::value,
65+
double>::type
66+
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
67+
4868
/**
4969
* from_chars for integer types.
5070
*/

include/fast_float/parse_number.h

Lines changed: 89 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -188,32 +188,17 @@ from_chars(UC const *first, UC const *last, T &value,
188188
parse_options_t<UC>(fmt));
189189
}
190190

191-
/**
192-
* This function overload takes parsed_number_string_t structure that is created
193-
* and populated either by from_chars_advanced function taking chars range and
194-
* parsing options or other parsing custom function implemented by user.
195-
*/
196-
template <typename T, typename UC>
197-
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
198-
from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
199-
200-
static_assert(is_supported_float_type<T>::value,
201-
"only some floating-point types are supported");
202-
static_assert(is_supported_char_type<UC>::value,
203-
"only char, wchar_t, char16_t and char32_t are supported");
204-
205-
from_chars_result_t<UC> answer;
206-
207-
answer.ec = std::errc(); // be optimistic
208-
answer.ptr = pns.lastmatch;
191+
template <typename T>
192+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
193+
clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
194+
T &value) noexcept {
209195
// The implementation of the Clinger's fast path is convoluted because
210196
// we want round-to-nearest in all cases, irrespective of the rounding mode
211197
// selected on the thread.
212198
// We proceed optimistically, assuming that detail::rounds_to_nearest()
213199
// returns true.
214-
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent &&
215-
pns.exponent <= binary_format<T>::max_exponent_fast_path() &&
216-
!pns.too_many_digits) {
200+
if (binary_format<T>::min_exponent_fast_path() <= exponent &&
201+
exponent <= binary_format<T>::max_exponent_fast_path()) {
217202
// Unfortunately, the conventional Clinger's fast path is only possible
218203
// when the system rounds to the nearest float.
219204
//
@@ -224,41 +209,64 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
224209
if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
225210
// We have that fegetround() == FE_TONEAREST.
226211
// Next is Clinger's fast path.
227-
if (pns.mantissa <= binary_format<T>::max_mantissa_fast_path()) {
228-
value = T(pns.mantissa);
229-
if (pns.exponent < 0) {
230-
value = value / binary_format<T>::exact_power_of_ten(-pns.exponent);
212+
if (mantissa <= binary_format<T>::max_mantissa_fast_path()) {
213+
value = T(mantissa);
214+
if (exponent < 0) {
215+
value = value / binary_format<T>::exact_power_of_ten(-exponent);
231216
} else {
232-
value = value * binary_format<T>::exact_power_of_ten(pns.exponent);
217+
value = value * binary_format<T>::exact_power_of_ten(exponent);
233218
}
234-
if (pns.negative) {
219+
if (is_negative) {
235220
value = -value;
236221
}
237-
return answer;
222+
return true;
238223
}
239224
} else {
240225
// We do not have that fegetround() == FE_TONEAREST.
241226
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's
242227
// proposal
243-
if (pns.exponent >= 0 &&
244-
pns.mantissa <=
245-
binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
228+
if (exponent >= 0 &&
229+
mantissa <= binary_format<T>::max_mantissa_fast_path(exponent)) {
246230
#if defined(__clang__) || defined(FASTFLOAT_32BIT)
247231
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
248-
if (pns.mantissa == 0) {
249-
value = pns.negative ? T(-0.) : T(0.);
250-
return answer;
232+
if (mantissa == 0) {
233+
value = is_negative ? T(-0.) : T(0.);
234+
return true;
251235
}
252236
#endif
253-
value = T(pns.mantissa) *
254-
binary_format<T>::exact_power_of_ten(pns.exponent);
255-
if (pns.negative) {
237+
value = T(mantissa) * binary_format<T>::exact_power_of_ten(exponent);
238+
if (is_negative) {
256239
value = -value;
257240
}
258-
return answer;
241+
return true;
259242
}
260243
}
261244
}
245+
return false;
246+
}
247+
248+
/**
249+
* This function overload takes parsed_number_string_t structure that is created
250+
* and populated either by from_chars_advanced function taking chars range and
251+
* parsing options or other parsing custom function implemented by user.
252+
*/
253+
template <typename T, typename UC>
254+
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
255+
from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
256+
static_assert(is_supported_float_type<T>::value,
257+
"only some floating-point types are supported");
258+
static_assert(is_supported_char_type<UC>::value,
259+
"only char, wchar_t, char16_t and char32_t are supported");
260+
261+
from_chars_result_t<UC> answer;
262+
263+
answer.ec = std::errc(); // be optimistic
264+
answer.ptr = pns.lastmatch;
265+
266+
if (!pns.too_many_digits &&
267+
clinger_fast_path_impl(pns.mantissa, pns.exponent, pns.negative, value))
268+
return answer;
269+
262270
adjusted_mantissa am =
263271
compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
264272
if (pns.too_many_digits && am.power2 >= 0) {
@@ -336,6 +344,49 @@ from_chars(UC const *first, UC const *last, T &value, int base) noexcept {
336344
return from_chars_advanced(first, last, value, options);
337345
}
338346

347+
FASTFLOAT_CONSTEXPR20 inline double
348+
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept {
349+
double value;
350+
if (clinger_fast_path_impl(mantissa, decimal_exponent, false, value))
351+
return value;
352+
353+
adjusted_mantissa am =
354+
compute_float<binary_format<double>>(decimal_exponent, mantissa);
355+
to_float(false, am, value);
356+
return value;
357+
}
358+
359+
FASTFLOAT_CONSTEXPR20 inline double
360+
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
361+
const bool is_negative = mantissa < 0;
362+
const uint64_t m = static_cast<uint64_t>(is_negative ? -mantissa : mantissa);
363+
364+
double value;
365+
if (clinger_fast_path_impl(m, decimal_exponent, is_negative, value))
366+
return value;
367+
368+
adjusted_mantissa am =
369+
compute_float<binary_format<double>>(decimal_exponent, m);
370+
to_float(is_negative, am, value);
371+
return value;
372+
}
373+
374+
// the following overloads are here to avoid surprising ambiguity for int,
375+
// unsigned, etc.
376+
template <typename Int>
377+
FASTFLOAT_CONSTEXPR20 inline typename std::enable_if<
378+
std::is_integral<Int>::value && !std::is_signed<Int>::value, double>::type
379+
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
380+
return integer_times_pow10(static_cast<uint64_t>(mantissa), decimal_exponent);
381+
}
382+
383+
template <typename Int>
384+
FASTFLOAT_CONSTEXPR20 inline typename std::enable_if<
385+
std::is_integral<Int>::value && std::is_signed<Int>::value, double>::type
386+
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
387+
return integer_times_pow10(static_cast<int64_t>(mantissa), decimal_exponent);
388+
}
389+
339390
template <typename T, typename UC>
340391
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
341392
from_chars_int_advanced(UC const *first, UC const *last, T &value,

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ fast_float_add_cpp_test(wide_char_test)
6161
fast_float_add_cpp_test(supported_chars_test)
6262
fast_float_add_cpp_test(example_test)
6363
fast_float_add_cpp_test(example_comma_test)
64+
fast_float_add_cpp_test(example_integer_times_pow10)
6465
fast_float_add_cpp_test(basictest)
6566
option(FASTFLOAT_CONSTEXPR_TESTS "Require constexpr tests (build will fail if the compiler won't support it)" OFF)
6667
if (FASTFLOAT_CONSTEXPR_TESTS)

0 commit comments

Comments
 (0)