0

The code below compiles just fine on Windows using VC++:

#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>


#include <iostream>

namespace mp = boost::multiprecision;
using BigFloat = mp::cpp_dec_float_50;
using BigInt = mp::uint256_t;

template <int decimals = 0, typename T> T floorBI(T const& v)
{
    static const T scale = pow(T(10), decimals);

    if (v.is_zero())
        return v;

    // ceil/floor is found via ADL and uses expression templates for
    // optimization
    if (v < 0)
        return ceil(v * scale) / scale;
    else
        // floor is found via ADL and uses expression templates for optimization
        return floor(v * scale) / scale;
}

int main()
{
    BigFloat A = 3;
    BigFloat B = 2; 

 static_cast<BigInt>(floorBI<0>(static_cast<BigFloat>(A) / static_cast<BigFloat>(B)));
    return 0;
}

, however with GCC (for Android and iOS) there's a problem seemingly with templates.

The particular error(s) for a template which 'should' be matched read(s)

..\boost/multiprecision/detail/default_ops.hpp:3745:18: note: candidate template ignored: could not match 'expression' against 'number' UNARY_OP_FUNCTOR(ceil, number_kind_floating_point) ^ ..\boost/multiprecision/detail/default_ops.hpp:3745:18: note: candidate template ignored: could not match 1 against 0 ..\boost/multiprecision/detail/default_ops.hpp:3745:18: note: candidate template ignored: requirement 'boost::multiprecision::number_category<boost::multiprecision::backends::cpp_int_backend<256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void> >::value == number_kind_floating_point' was not satisfied [with Backend = boost::multiprecision::backends::cpp_int_backend<256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>] ..\boost/multiprecision/detail/default_ops.hpp:3745:18: note: candidate template ignored: could not match 1 against 0

VC++ handles all of it 'just fine'.

Waiting for @Sehe to come around;]

Vega4
  • 969
  • 1
  • 11
  • 25
  • @Sehe, surely? MSVC very much "lazy-evaluates" templates. (It shouldn't). Are you sure you are including the necessary files in the correct place? Us lesser mortals might need to see a bit more code to be sure. – Bathsheba Feb 09 '22 at 18:07
  • Of course, @Sehe it was to be;p – Vega4 Feb 09 '22 at 18:12
  • 1
    The code does not compile on my machine. I suspect this code is incomplete, and not a [mcve]. For one, it appears to be missing an `#include`. And `T` is undefined. – Eljay Feb 09 '22 at 18:14
  • Included the non-standard includes for you fellas. – Vega4 Feb 09 '22 at 18:16
  • That looks weird. (a) I think your should be able to find rounding implemented. (b) You're rounding strictly integral expressions. How? Why. Integers don't have decimal fractions. Full stop. – sehe Feb 09 '22 at 18:39
  • Got your point still, if you replace integer with multiprecision float the result is the same. As you probably noticed the function is the one implemented by you in another post @Sehe. It wouldn't compile through GCC. – Vega4 Feb 09 '22 at 18:42
  • https://stackoverflow.com/questions/23032596/force-cpp-dec-float-to-round-down @Sehe, here it goes it's the same. You've mentioned that I should be able to find a rounding function, you mean there's now a better alternative to the one you've proposed over there? – Vega4 Feb 09 '22 at 18:49
  • "compiles just fine on Windows using VC++" no it doesn't, please post **exactly** the code that you tested, not something that vaguely looks similar. – Marc Glisse Feb 09 '22 at 18:54
  • Most likely the problem is the same as using `auto` with expression templates. You can't use T like a normal type. You could use `typename T::result_type` (or whatever the official syntax is for this). Or force the argument type `floorBI<0,BigFloat>`. – Marc Glisse Feb 09 '22 at 18:58
  • @Vega4 the linked answer is about cpp_dec_float: a floating point type. What floating point representation do you wish to use? – sehe Feb 09 '22 at 19:04
  • The problem is at the line ceil(v * scale) / scale with compiler not being able to match the appropriate template candidate of ceil (just like in the print-out I've provided) – Vega4 Feb 09 '22 at 19:04
  • I know. The compiler agrees with me: it's silly to use `ceil` or `floor` on integers. – sehe Feb 09 '22 at 19:06
  • The code posted still trivially fails to compile, because you are missing a "using" there. Do not post pseudocode. Copy & paste code you have *verified* to exhibit the behavior described in the Q. – DevSolar Feb 09 '22 at 19:15
  • Oh dang the question code was edited more now? Geez. I missed that too – sehe Feb 09 '22 at 19:16
  • I've included a fully working (VC++) reproducible example. – Vega4 Feb 09 '22 at 19:22
  • Strangely, as soon as one removes static-casts from A and B (which are BigFloats already) the compiler goes wild (VC++) – Vega4 Feb 09 '22 at 19:23
  • Strangely you post those comments *after* I posted my answer. – sehe Feb 09 '22 at 19:35
  • @Sehe, kindly do note, that I've had the code working fine in VC++ beforehand. I had only trouble with GCC. No ill intentions here. I was updating the working minimal reproducible example. I also wrote in my initial question that there was a problem with templates and provided print-out clearly indicating that a call to ceil could not be matched. I see no point in highlighting the fact that an integer was attempted to be rounded as it's trivial to assume that THAT clearly must had not been the culprit scenario. With all that said kindly thank you for trying to help! – Vega4 Feb 09 '22 at 20:55
  • Nobody is focusing on that. It **was** the problem though, even according to the compiler message which is still in your own question: _`requirement number_category >::value == number_kind_floating_point was not satisfied`_. So, yeah, you ended up changing the question completely, and I've answered it. No worries. – sehe Feb 09 '22 at 21:07
  • @Sehe. I've been porting code from VC++ to a mobile app. Due to some differences, I decided to go, function by function, instead of copying the entire code-page. I can now clearly see that I 'forgot' to include static_casts to BigFloats around coefficients passed to floorBI, which of course ended up being evaluated as an integer. And *I do so to have control over how rounding behaves when dividing large integers*. – Vega4 Feb 09 '22 at 21:17
  • The only thing which leaves me perplexed, is why the compiler requires additional static_casts around coefficients if the 'proper' type (BigFloat) is already specified in variables' declaration (VC++) one may check this by following the reproducible example, if static_cast(A) / static_cast(B) is changed to A / B the code fails to compile. Even though both A and B were BigFloats to begin with. – Vega4 Feb 09 '22 at 21:21
  • Did my answer not help? You can see exactly what `T` is in the instantiation of `floorBI`. I formatted it nicely in my answer. – sehe Feb 09 '22 at 21:51

1 Answers1

2

Like many people already suggested, expression templates are the culprit:

T is deduced as

boost::multiprecision::detail::expression<
    boost::multiprecision::detail::divides,
    boost::multiprecision::number<
        boost::multiprecision::backends::cpp_dec_float<50>>,
    boost::multiprecision::detail::expression<
        boost::multiprecision::detail::multiply_immediates,
        boost::multiprecision::number<
            boost::multiprecision::backends::cpp_dec_float<50>>,
        boost::multiprecision::number<
            boost::multiprecision::backends::cpp_dec_float<50>>,
        void, void>,
    void, void>

Which is not constructible from int: T(10). Instead, either force the type:

std::cout << floorBI<0>(BigFloat(A / (B * C))) << "\n";

Or be smarter about deducing the type.

DEMONSTRUCTION

Live On Coliru

#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/number.hpp>
#include <iostream>

namespace bmp = boost::multiprecision;
using BigFloat = bmp::cpp_dec_float_50;

template <int decimals = 0, typename T>
std::enable_if_t<not bmp::is_number_expression<T>::value, T>
floorBI(T const& v)
{
    static const T scale = pow(T(10), decimals);

    if (v.is_zero())
        return v;

    // ceil/floor is found via ADL and uses expression templates for
    // optimization
    if (v < 0)
        return ceil(v * scale) / scale;
    else
        // floor is found via ADL and uses expression templates for optimization
        return floor(v * scale) / scale;
}

template <int decimals = 0, typename Expr>
auto floorBI(Expr const& expr,
    std::enable_if_t<bmp::is_number_expression<Expr>::value, void>* enable = nullptr)
{
    return floorBI<decimals, typename Expr::result_type>(expr);
}


int main()
{
    BigFloat A(3), B(2), C(2);
    std::cout <<  floorBI<1>(BigFloat(A / (B * C))) << "\n";
    std::cout <<  floorBI<1>(A / (B * C)) << "\n";
    std::cout <<  floorBI<2>(BigFloat(A / (B * C))) << "\n";
    std::cout <<  floorBI<2>(A / (B * C)) << "\n";
    B *= -1;
    std::cout <<  floorBI<1>(BigFloat(A / (B * C))) << "\n";
    std::cout <<  floorBI<1>(A / (B * C)) << "\n";
    std::cout <<  floorBI<2>(BigFloat(A / (B * C))) << "\n";
    std::cout <<  floorBI<2>(A / (B * C)) << "\n";
}

Prints

0.7
0.7
0.75
0.75
-0.7
-0.7
-0.75
-0.75
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Added a demonstration of what "being smarter about deducing the argument type" could look like **[Live On Coliru](http://coliru.stacked-crooked.com/a/3b0cca102bf4df42)** – sehe Feb 09 '22 at 19:34