-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Try first cut at issue 368 #371
Changes from 2 commits
444f457
90b312b
4424120
6c07fa4
2aa5252
45d93a2
8d49460
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -522,6 +522,141 @@ class cpp_dec_float | |
ar& boost::make_nvp("precision", prec_elem); | ||
} | ||
|
||
void eval_round_self() | ||
{ | ||
const bool needs_rounding = (((isfinite)() == true) && (iszero() == false)); | ||
|
||
if(needs_rounding) | ||
{ | ||
using local_size_type = typename array_type::size_type; | ||
using local_limb_type = typename array_type::value_type; | ||
|
||
std::int32_t digits_limb_0 = 0; | ||
|
||
local_limb_type tmp_limb_0 = data[0U]; | ||
|
||
// Manually count the number of base-10 digits on the zero'th limb. | ||
while(tmp_limb_0 > 0U) | ||
{ | ||
tmp_limb_0 /= 10U; | ||
|
||
++digits_limb_0; | ||
} | ||
|
||
constexpr std::int32_t local_max_digits10 = | ||
std::int32_t | ||
( | ||
cpp_dec_float_digits10 + 4 | ||
); | ||
|
||
const std::int32_t digits_limb_1_to_n = local_max_digits10 - digits_limb_0; | ||
|
||
// Find the index of the element that contains the least-significant base-10 digit. | ||
std::int32_t least_digit_idx = std::int32_t( (digits_limb_1_to_n / cpp_dec_float_elem_digits10) | ||
+ ((digits_limb_1_to_n % cpp_dec_float_elem_digits10) != 0)); | ||
|
||
// Set the index of the element that contains the rounding base-10 digit. | ||
std::int32_t round_digit_idx = | ||
std::int32_t(((digits_limb_1_to_n % cpp_dec_float_elem_digits10) != 0) | ||
? least_digit_idx | ||
: least_digit_idx + 1); | ||
|
||
// Find the base-10 order (position) of the least-significant base-10 digit. | ||
std::int32_t least_digit_pos = | ||
(std::int32_t(digits_limb_1_to_n % cpp_dec_float_elem_digits10) != 0) | ||
? std::int32_t | ||
( | ||
cpp_dec_float_elem_digits10 | ||
- std::int32_t(digits_limb_1_to_n % cpp_dec_float_elem_digits10) | ||
) | ||
: 0; | ||
|
||
// Find the base-10 order (position) of the rounding base-10 digit. | ||
std::int32_t round_digit_pos = | ||
std::int32_t((least_digit_pos != 0) ? least_digit_pos - 1 : cpp_dec_float_elem_digits10 - 1); | ||
|
||
// Get the value of the least-significant base-10 digit. | ||
const std::uint8_t least_digit_value = | ||
detail::digit_at_pos_in_limb | ||
( | ||
data[local_size_type(least_digit_idx)], | ||
unsigned(least_digit_pos) | ||
); | ||
|
||
// TBD: Remove unused debug-helper variable. | ||
static_cast<void>(least_digit_value); | ||
|
||
// Get the value of the rounding base-10 digit. | ||
const std::uint8_t round_digit_value = | ||
detail::digit_at_pos_in_limb | ||
( | ||
data[local_size_type(round_digit_idx)], | ||
unsigned(round_digit_pos) | ||
); | ||
|
||
local_limb_type least_digit_p10 = detail::pow10_maker(std::uint32_t(least_digit_pos)); | ||
local_limb_type round_digit_p10 = detail::pow10_maker(std::uint32_t(round_digit_pos)); | ||
|
||
// TBD: Remove unused debug-helper variable. | ||
static_cast<void>(round_digit_p10); | ||
|
||
// Clear the lower base-10 digits of the rounded element. | ||
data[local_size_type(least_digit_idx)] -= local_limb_type(data[local_size_type(least_digit_idx)] % least_digit_p10); | ||
|
||
// Clear the lower base-10 limbs. | ||
if(local_size_type(least_digit_idx + 1) < data.size() - 1U) | ||
{ | ||
std::fill(data.begin() + local_size_type(least_digit_idx + 1), data.end(), local_limb_type(0U)); | ||
} | ||
|
||
// Perform round-to-nearest with no tie-breaking whatsoever. | ||
if(round_digit_value >= 5U) | ||
{ | ||
data[local_size_type(least_digit_idx)] += least_digit_p10; | ||
|
||
// There is a carry from rounding up. | ||
std::uint_fast8_t carry_out = | ||
((data[local_size_type(least_digit_idx)] >= cpp_dec_float_elem_mask) | ||
? static_cast<std::uint_fast8_t>(1U) | ||
: static_cast<std::uint_fast8_t>(0U)); | ||
|
||
// Propogate the carry into the limbs of higher significance as needed. | ||
if(carry_out != 0U) | ||
{ | ||
data[local_size_type(least_digit_idx)] -= cpp_dec_float_elem_mask; | ||
|
||
--least_digit_idx; | ||
|
||
for( ; least_digit_idx >= 0 && (carry_out != 0U); --least_digit_idx) | ||
{ | ||
const local_limb_type tt = local_limb_type(data[local_size_type(least_digit_idx)] + local_limb_type(carry_out)); | ||
|
||
carry_out = ((tt >= cpp_dec_float_elem_mask) ? static_cast<std::uint_fast8_t>(1U) | ||
: static_cast<std::uint_fast8_t>(0U)); | ||
|
||
data[local_size_type(least_digit_idx)] = | ||
static_cast<local_limb_type>(tt - ((carry_out != 0U) ? cpp_dec_float_elem_mask | ||
: static_cast<local_limb_type>(0U))); | ||
} | ||
|
||
if((least_digit_idx < 0) && (carry_out != 0U)) | ||
{ | ||
// In rare cases, propagation of the carry reaches the zero'th limb | ||
// of highest significance, and we must shift the data, create a new limb | ||
// with the carry value of 1 and adjust the exponent accordingly. | ||
std::copy_backward(data.cbegin(), | ||
data.cend() - 1, | ||
data.end()); | ||
|
||
data[0U] = carry_out; | ||
|
||
exp = static_cast<exponent_type>(exp + static_cast<exponent_type>(cpp_dec_float_elem_digits10)); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
private: | ||
static bool data_elem_is_non_zero_predicate(const std::uint32_t& d) { return (d != static_cast<std::uint32_t>(0u)); } | ||
static bool data_elem_is_non_nine_predicate(const std::uint32_t& d) { return (d != static_cast<std::uint32_t>(cpp_dec_float::cpp_dec_float_elem_mask - 1)); } | ||
|
@@ -1643,10 +1778,15 @@ boost::long_long_type cpp_dec_float<Digits10, ExponentType, Allocator>::extract_ | |
else | ||
{ | ||
// Extract the data into an boost::ulong_long_type value. | ||
cpp_dec_float<Digits10, ExponentType, Allocator> xn(extract_integer_part()); | ||
cpp_dec_float<Digits10, ExponentType, Allocator> xn(*this); | ||
|
||
xn.eval_round_self(); | ||
|
||
if (xn.isneg()) | ||
xn.negate(); | ||
|
||
xn = xn.extract_integer_part(); | ||
|
||
val = static_cast<boost::ulong_long_type>(xn.data[0]); | ||
|
||
const std::int32_t imax = (std::min)(static_cast<std::int32_t>(static_cast<std::int32_t>(xn.exp) / cpp_dec_float_elem_digits10), static_cast<std::int32_t>(cpp_dec_float_elem_number - static_cast<std::int32_t>(1))); | ||
|
@@ -1688,35 +1828,40 @@ boost::ulong_long_type cpp_dec_float<Digits10, ExponentType, Allocator>::extract | |
{ | ||
return static_cast<boost::ulong_long_type>(extract_signed_long_long()); | ||
} | ||
|
||
if (exp < static_cast<exponent_type>(0)) | ||
else if (exp < static_cast<exponent_type>(0)) | ||
{ | ||
return static_cast<boost::ulong_long_type>(0u); | ||
} | ||
|
||
const cpp_dec_float<Digits10, ExponentType, Allocator> xn(extract_integer_part()); | ||
|
||
boost::ulong_long_type val; | ||
|
||
if (xn.compare(ulong_long_max()) > 0) | ||
{ | ||
return (std::numeric_limits<boost::ulong_long_type>::max)(); | ||
} | ||
else | ||
{ | ||
// Extract the data into an boost::ulong_long_type value. | ||
val = static_cast<boost::ulong_long_type>(xn.data[0]); | ||
cpp_dec_float<Digits10, ExponentType, Allocator> xn(*this); | ||
|
||
const std::int32_t imax = (std::min)(static_cast<std::int32_t>(static_cast<std::int32_t>(xn.exp) / cpp_dec_float_elem_digits10), static_cast<std::int32_t>(cpp_dec_float_elem_number - static_cast<std::int32_t>(1))); | ||
xn.eval_round_self(); | ||
|
||
for (std::int32_t i = static_cast<std::int32_t>(1); i <= imax; i++) | ||
xn = xn.extract_integer_part(); | ||
|
||
boost::ulong_long_type val; | ||
|
||
if (xn.compare(ulong_long_max()) > 0) | ||
{ | ||
val *= static_cast<boost::ulong_long_type>(cpp_dec_float_elem_mask); | ||
val += static_cast<boost::ulong_long_type>(xn.data[i]); | ||
return (std::numeric_limits<boost::ulong_long_type>::max)(); | ||
} | ||
else | ||
{ | ||
// Extract the data into an boost::ulong_long_type value. | ||
val = static_cast<boost::ulong_long_type>(xn.data[0]); | ||
|
||
const std::int32_t imax = (std::min)(static_cast<std::int32_t>(static_cast<std::int32_t>(xn.exp) / cpp_dec_float_elem_digits10), static_cast<std::int32_t>(cpp_dec_float_elem_number - static_cast<std::int32_t>(1))); | ||
|
||
for (std::int32_t i = static_cast<std::int32_t>(1); i <= imax; i++) | ||
{ | ||
val *= static_cast<boost::ulong_long_type>(cpp_dec_float_elem_mask); | ||
val += static_cast<boost::ulong_long_type>(xn.data[i]); | ||
} | ||
} | ||
} | ||
|
||
return val; | ||
return val; | ||
} | ||
} | ||
|
||
template <unsigned Digits10, class ExponentType, class Allocator> | ||
|
@@ -3091,37 +3236,73 @@ template <unsigned Digits10, class ExponentType, class Allocator> | |
inline void eval_floor(cpp_dec_float<Digits10, ExponentType, Allocator>& result, const cpp_dec_float<Digits10, ExponentType, Allocator>& x) | ||
{ | ||
result = x; | ||
if (!(x.isfinite)() || x.isint()) | ||
|
||
if((x.isfinite)() == false) | ||
{ | ||
if ((x.isnan)()) | ||
errno = EDOM; | ||
return; | ||
} | ||
else | ||
{ | ||
result.eval_round_self(); | ||
|
||
if (x.isneg()) | ||
result -= cpp_dec_float<Digits10, ExponentType, Allocator>::one(); | ||
result = result.extract_integer_part(); | ||
if(result.isint()) | ||
{ | ||
; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Indeed. These sequences have been simlified in recent push(es). |
||
} | ||
else | ||
{ | ||
if(result.isneg()) | ||
{ | ||
result -= cpp_dec_float<Digits10, ExponentType, Allocator>::one(); | ||
} | ||
else | ||
{ | ||
; | ||
} | ||
|
||
result = result.extract_integer_part(); | ||
} | ||
} | ||
} | ||
|
||
template <unsigned Digits10, class ExponentType, class Allocator> | ||
inline void eval_ceil(cpp_dec_float<Digits10, ExponentType, Allocator>& result, const cpp_dec_float<Digits10, ExponentType, Allocator>& x) | ||
{ | ||
result = x; | ||
if (!(x.isfinite)() || x.isint()) | ||
|
||
if ((x.isfinite)() == false) | ||
{ | ||
if ((x.isnan)()) | ||
errno = EDOM; | ||
return; | ||
} | ||
else | ||
{ | ||
result.eval_round_self(); | ||
|
||
if(result.isint()) | ||
{ | ||
; | ||
} | ||
else | ||
{ | ||
if(result.isneg()) | ||
{ | ||
} | ||
else | ||
{ | ||
result += cpp_dec_float<Digits10, ExponentType, Allocator>::one(); | ||
} | ||
|
||
if (!x.isneg()) | ||
result += cpp_dec_float<Digits10, ExponentType, Allocator>::one(); | ||
result = result.extract_integer_part(); | ||
result = result.extract_integer_part(); | ||
} | ||
} | ||
} | ||
|
||
template <unsigned Digits10, class ExponentType, class Allocator> | ||
inline void eval_trunc(cpp_dec_float<Digits10, ExponentType, Allocator>& result, const cpp_dec_float<Digits10, ExponentType, Allocator>& x) | ||
{ | ||
|
||
if (x.isint() || !(x.isfinite)()) | ||
{ | ||
result = x; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/////////////////////////////////////////////////////////////////////////////// | ||
// Copyright 2021 Christopher Kormanyos. Distributed under the Boost | ||
// Software License, Version 1.0. (See accompanying file | ||
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
|
||
#include <boost/multiprecision/cpp_dec_float.hpp> | ||
|
||
#include "test.hpp" | ||
|
||
namespace local | ||
{ | ||
template<typename DecimalType> | ||
void test_ceil_rounding() | ||
{ | ||
using decimal_type = DecimalType; | ||
|
||
bool result_is_ok = true; | ||
|
||
for(std::uint32_t lo_index = 101U; lo_index < 1010U; lo_index += 7U) | ||
{ | ||
for(std::uint32_t hi_index = 10001U; hi_index < 100010U; hi_index += 17U) | ||
{ | ||
const std::uint32_t lo_hi = lo_index * hi_index; | ||
|
||
const decimal_type a = decimal_type { lo_hi } / decimal_type { lo_index }; | ||
const decimal_type b = ceil(a); | ||
|
||
const std::string str_a = boost::lexical_cast<std::string>(a); | ||
const std::string str_b = boost::lexical_cast<std::string>(b); | ||
|
||
result_is_ok &= (str_a == str_b); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Questions: why are we comparing strings rather than values, and should we also test inputs which are not integer? Negative values as well, plus floor too I suspect ;) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In this PR, there is rounding on
It seems like there are already-existing rounding tests which do this.
Understood. Done in resent push(es). |
||
|
||
if(result_is_ok == false) | ||
{ | ||
break; | ||
} | ||
} | ||
} | ||
|
||
BOOST_CHECK_EQUAL(result_is_ok, true); | ||
} | ||
} | ||
|
||
int main(int, char**) | ||
{ | ||
bool result_is_ok = true; | ||
|
||
{ | ||
using decimal = boost::multiprecision::number<boost::multiprecision::cpp_dec_float<10>, | ||
boost::multiprecision::et_off>; | ||
|
||
local::test_ceil_rounding<decimal>(); | ||
} | ||
|
||
{ | ||
using decimal = boost::multiprecision::number<boost::multiprecision::cpp_dec_float<12>, | ||
boost::multiprecision::et_off>; | ||
|
||
local::test_ceil_rounding<decimal>(); | ||
} | ||
|
||
return boost::report_errors(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the bit that caused me to assume that correct round was next to impossible to do efficiently in this type, but maybe it's not so bad? I can't help but wonder if there are more efficient implementations - maybe binary search comparisons? Or can we even do table lookup based on the most-significant-bit? Just thinking out loud here....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, it sure is proving to be unruly. But there are some new efficiency provisions.
constexpr
table lookup has also been created for quick calculations of the power(s) of ten in similar matters.