Compile time positive and negative powers of two

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
5
down vote

favorite












I'm getting started on template meta-programming and I wrote a template to compute compile-time positive and negative float powers of 2. I would like some feedback on readability and overall code complexity as I feel like I might be defining too many structures for such a simple computation. The code has to be C++98 compatible.



// Sign of an integer
template<bool b> struct SIGN static const int value = ( b * 2 ) - 1; ;

// Positive powers of two (fast exponentiation)
template<unsigned int N> struct POW2_POS
static const float value =
POW2_POS<N%2>::value
* POW2_POS<N/2>::value
* POW2_POS<N/2>::value;
;
template<> struct POW2_POS <1> static const float value = 2.0f; ;
template<> struct POW2_POS <0> static const float value = 1.0f; ;

// Negative powers of two
template<unsigned int N> struct POW2_NEG static const float value = 1.0f / POW2_POS<N>::value; ;

// Calls POW2_POS or POW2_NEG depending on boolean parameter
template<bool b, unsigned int N> struct POW2_SIGNED;
template<unsigned int N> struct POW2_SIGNED<true, N> static const float value = POW2_POS<N>::value;;
template<unsigned int N> struct POW2_SIGNED<false, N> static const float value = POW2_NEG<N>::value;;

// Integer power of two
template<int N> struct POW2
static const float value = POW2_SIGNED<(N>=0), SIGN<(N>=0)>::value * N>::value;
;


Example program



#include <iostream>
int main()

std::cout<<POW2<0>::value <<"n";
std::cout<<POW2<1>::value <<"n";
std::cout<<POW2<4>::value <<"n";
std::cout<<POW2<7>::value <<"n";
std::cout<<POW2<-4>::value <<"n";



Output



1
2
16
128
0.0625






share|improve this question

















  • 2




    Prefer to use double instead of float. Because these are compile time constants down casting to float will cost you nothing but up casting to double from float will give you less precision than you would have had had you used doubles in your template.
    – Emily L.
    May 25 at 0:19







  • 1




    Also, all capital letters in identifies are usually reserved for macros. I would advise against using it for other identifiers.
    – Emily L.
    May 25 at 0:21






  • 2




    Why do you want to “get started” with the stuff we are finally glad to be rid of?
    – JDługosz
    May 25 at 9:12










  • Because unfortunately I'm forced to work on a very old compiler.
    – Rch
    May 25 at 10:46










  • @TobySpeight sure, I wrote one up. It was in the middle of the night and I just wanted to leave quick comments on my phone before shuteye.
    – Emily L.
    May 25 at 13:26
















up vote
5
down vote

favorite












I'm getting started on template meta-programming and I wrote a template to compute compile-time positive and negative float powers of 2. I would like some feedback on readability and overall code complexity as I feel like I might be defining too many structures for such a simple computation. The code has to be C++98 compatible.



// Sign of an integer
template<bool b> struct SIGN static const int value = ( b * 2 ) - 1; ;

// Positive powers of two (fast exponentiation)
template<unsigned int N> struct POW2_POS
static const float value =
POW2_POS<N%2>::value
* POW2_POS<N/2>::value
* POW2_POS<N/2>::value;
;
template<> struct POW2_POS <1> static const float value = 2.0f; ;
template<> struct POW2_POS <0> static const float value = 1.0f; ;

// Negative powers of two
template<unsigned int N> struct POW2_NEG static const float value = 1.0f / POW2_POS<N>::value; ;

// Calls POW2_POS or POW2_NEG depending on boolean parameter
template<bool b, unsigned int N> struct POW2_SIGNED;
template<unsigned int N> struct POW2_SIGNED<true, N> static const float value = POW2_POS<N>::value;;
template<unsigned int N> struct POW2_SIGNED<false, N> static const float value = POW2_NEG<N>::value;;

// Integer power of two
template<int N> struct POW2
static const float value = POW2_SIGNED<(N>=0), SIGN<(N>=0)>::value * N>::value;
;


Example program



#include <iostream>
int main()

std::cout<<POW2<0>::value <<"n";
std::cout<<POW2<1>::value <<"n";
std::cout<<POW2<4>::value <<"n";
std::cout<<POW2<7>::value <<"n";
std::cout<<POW2<-4>::value <<"n";



Output



1
2
16
128
0.0625






share|improve this question

















  • 2




    Prefer to use double instead of float. Because these are compile time constants down casting to float will cost you nothing but up casting to double from float will give you less precision than you would have had had you used doubles in your template.
    – Emily L.
    May 25 at 0:19







  • 1




    Also, all capital letters in identifies are usually reserved for macros. I would advise against using it for other identifiers.
    – Emily L.
    May 25 at 0:21






  • 2




    Why do you want to “get started” with the stuff we are finally glad to be rid of?
    – JDługosz
    May 25 at 9:12










  • Because unfortunately I'm forced to work on a very old compiler.
    – Rch
    May 25 at 10:46










  • @TobySpeight sure, I wrote one up. It was in the middle of the night and I just wanted to leave quick comments on my phone before shuteye.
    – Emily L.
    May 25 at 13:26












up vote
5
down vote

favorite









up vote
5
down vote

favorite











I'm getting started on template meta-programming and I wrote a template to compute compile-time positive and negative float powers of 2. I would like some feedback on readability and overall code complexity as I feel like I might be defining too many structures for such a simple computation. The code has to be C++98 compatible.



// Sign of an integer
template<bool b> struct SIGN static const int value = ( b * 2 ) - 1; ;

// Positive powers of two (fast exponentiation)
template<unsigned int N> struct POW2_POS
static const float value =
POW2_POS<N%2>::value
* POW2_POS<N/2>::value
* POW2_POS<N/2>::value;
;
template<> struct POW2_POS <1> static const float value = 2.0f; ;
template<> struct POW2_POS <0> static const float value = 1.0f; ;

// Negative powers of two
template<unsigned int N> struct POW2_NEG static const float value = 1.0f / POW2_POS<N>::value; ;

// Calls POW2_POS or POW2_NEG depending on boolean parameter
template<bool b, unsigned int N> struct POW2_SIGNED;
template<unsigned int N> struct POW2_SIGNED<true, N> static const float value = POW2_POS<N>::value;;
template<unsigned int N> struct POW2_SIGNED<false, N> static const float value = POW2_NEG<N>::value;;

// Integer power of two
template<int N> struct POW2
static const float value = POW2_SIGNED<(N>=0), SIGN<(N>=0)>::value * N>::value;
;


Example program



#include <iostream>
int main()

std::cout<<POW2<0>::value <<"n";
std::cout<<POW2<1>::value <<"n";
std::cout<<POW2<4>::value <<"n";
std::cout<<POW2<7>::value <<"n";
std::cout<<POW2<-4>::value <<"n";



Output



1
2
16
128
0.0625






share|improve this question













I'm getting started on template meta-programming and I wrote a template to compute compile-time positive and negative float powers of 2. I would like some feedback on readability and overall code complexity as I feel like I might be defining too many structures for such a simple computation. The code has to be C++98 compatible.



// Sign of an integer
template<bool b> struct SIGN static const int value = ( b * 2 ) - 1; ;

// Positive powers of two (fast exponentiation)
template<unsigned int N> struct POW2_POS
static const float value =
POW2_POS<N%2>::value
* POW2_POS<N/2>::value
* POW2_POS<N/2>::value;
;
template<> struct POW2_POS <1> static const float value = 2.0f; ;
template<> struct POW2_POS <0> static const float value = 1.0f; ;

// Negative powers of two
template<unsigned int N> struct POW2_NEG static const float value = 1.0f / POW2_POS<N>::value; ;

// Calls POW2_POS or POW2_NEG depending on boolean parameter
template<bool b, unsigned int N> struct POW2_SIGNED;
template<unsigned int N> struct POW2_SIGNED<true, N> static const float value = POW2_POS<N>::value;;
template<unsigned int N> struct POW2_SIGNED<false, N> static const float value = POW2_NEG<N>::value;;

// Integer power of two
template<int N> struct POW2
static const float value = POW2_SIGNED<(N>=0), SIGN<(N>=0)>::value * N>::value;
;


Example program



#include <iostream>
int main()

std::cout<<POW2<0>::value <<"n";
std::cout<<POW2<1>::value <<"n";
std::cout<<POW2<4>::value <<"n";
std::cout<<POW2<7>::value <<"n";
std::cout<<POW2<-4>::value <<"n";



Output



1
2
16
128
0.0625








share|improve this question












share|improve this question




share|improve this question








edited May 26 at 3:47









Jamal♦

30.1k11114225




30.1k11114225









asked May 24 at 23:26









Rch

383




383







  • 2




    Prefer to use double instead of float. Because these are compile time constants down casting to float will cost you nothing but up casting to double from float will give you less precision than you would have had had you used doubles in your template.
    – Emily L.
    May 25 at 0:19







  • 1




    Also, all capital letters in identifies are usually reserved for macros. I would advise against using it for other identifiers.
    – Emily L.
    May 25 at 0:21






  • 2




    Why do you want to “get started” with the stuff we are finally glad to be rid of?
    – JDługosz
    May 25 at 9:12










  • Because unfortunately I'm forced to work on a very old compiler.
    – Rch
    May 25 at 10:46










  • @TobySpeight sure, I wrote one up. It was in the middle of the night and I just wanted to leave quick comments on my phone before shuteye.
    – Emily L.
    May 25 at 13:26












  • 2




    Prefer to use double instead of float. Because these are compile time constants down casting to float will cost you nothing but up casting to double from float will give you less precision than you would have had had you used doubles in your template.
    – Emily L.
    May 25 at 0:19







  • 1




    Also, all capital letters in identifies are usually reserved for macros. I would advise against using it for other identifiers.
    – Emily L.
    May 25 at 0:21






  • 2




    Why do you want to “get started” with the stuff we are finally glad to be rid of?
    – JDługosz
    May 25 at 9:12










  • Because unfortunately I'm forced to work on a very old compiler.
    – Rch
    May 25 at 10:46










  • @TobySpeight sure, I wrote one up. It was in the middle of the night and I just wanted to leave quick comments on my phone before shuteye.
    – Emily L.
    May 25 at 13:26







2




2




Prefer to use double instead of float. Because these are compile time constants down casting to float will cost you nothing but up casting to double from float will give you less precision than you would have had had you used doubles in your template.
– Emily L.
May 25 at 0:19





Prefer to use double instead of float. Because these are compile time constants down casting to float will cost you nothing but up casting to double from float will give you less precision than you would have had had you used doubles in your template.
– Emily L.
May 25 at 0:19





1




1




Also, all capital letters in identifies are usually reserved for macros. I would advise against using it for other identifiers.
– Emily L.
May 25 at 0:21




Also, all capital letters in identifies are usually reserved for macros. I would advise against using it for other identifiers.
– Emily L.
May 25 at 0:21




2




2




Why do you want to “get started” with the stuff we are finally glad to be rid of?
– JDługosz
May 25 at 9:12




Why do you want to “get started” with the stuff we are finally glad to be rid of?
– JDługosz
May 25 at 9:12












Because unfortunately I'm forced to work on a very old compiler.
– Rch
May 25 at 10:46




Because unfortunately I'm forced to work on a very old compiler.
– Rch
May 25 at 10:46












@TobySpeight sure, I wrote one up. It was in the middle of the night and I just wanted to leave quick comments on my phone before shuteye.
– Emily L.
May 25 at 13:26




@TobySpeight sure, I wrote one up. It was in the middle of the night and I just wanted to leave quick comments on my phone before shuteye.
– Emily L.
May 25 at 13:26










1 Answer
1






active

oldest

votes

















up vote
3
down vote



accepted










Your code is not C++98 compliant



Standards prior to C++11 only allows const integer static member initialisation (see here).



So unfortunately your code is not C++98 compliant and in fact does not compile. So technically your code is off-topic on this site. As it is not working as intended. Your code probably only compiles because you're using a compiler that has an extension to allow non integer const static members.



To get around this typically you would define a static method that returns the value. In fact, this is why std::numeric_limits<T>::max() is a function instead of a static member value. However these cannot be evaluated in a constant expression context. You need C++11 constexpr for that afaik.



General advice



Some general advice. Template parameters are compile time constants by definition. Any sensible compiler should do any necessary casts during compile time so you will not get a run-time penalty for using higher precision.



For this reason I advice that you use the largest types possible in your template to get the most precision at no additional cost. I.e. change float to double and int to long long.



You should consider wrapping the templates that are not intended for use by the user in a suitable namespace. Typically I use a nested namespace called detail.



Also identifiers in ALL_CAPS_WITH_UNDERSCORES are typically reserved for macros. So I advice against using this naming convention for your identifiers as it will confuse many readers.



Better implementation



If we restrict ourselves to the bits of the code that are C++98 compliant, then we can have a much easier implementation for the integer exponentiation.



template<unsigned long long exponent>
struct ipow_2
static const unsigned long long value = (1 << exponent);
;


If you want a C++98 compliant negative exponent you can use:



template<long long exponent>
struct pow_2
static double value()
if (exponent == 0)
return 1;

else if (exponent > 0)
double half = pow_2<exponent/2>::value();
return (1<<(exponent&1))*half*half;

return 1 / pow_2<-exponent>::value();
;
;


But be aware that the same caveat for non-integer const initialisation still applies so it's not hugely useful in TMP for example.



If we accept use C++11 you can simplify and generalize to:



// Tail call optimization will transform this into the equivalent 
// loop
constexpr int64_t ipow_p(int64_t base, int exp, int64_t ans = 1)
return exp < 1 ?
ans : ipow_p(base*base, exp/2, (exp % 2) ? ans*base : ans);


constexpr double ipow(int base, int exp)
return exp > 0 ? ipow_p(base, exp) : 1.0 / ipow_p(base, -exp);



This is usable in a constant (TMP) context thanks to the constexpr qualifier and At -O2 the call will be expanded to a constant.






share|improve this answer























  • Thank you, this explains a lot. I was very surprised to see that this code didn't compile on c++11, I assume it's because it didn't have the same extension. I'll have to figure out what exactly is allowed on the compiler I work with.
    – Rch
    May 25 at 16:20










  • Regarding your solution, I have an issue with the fact that it doesn't allow for exponents higher than the size of a long (which should be shorter than the maximum exponent of a double or even a float).
    – Rch
    May 25 at 16:23










  • @Rch "...didn't compile on C++11" The standard is irrelevant here, your code is not valid, standard C++ in any version of the language. I used clang to compile which doesn't have the extensions that GCC has. GCC should show the same behavior if you pass -Wall -Wextra -pedantic -Werror... well, at least recent versions.
    – Emily L.
    May 25 at 18:32










  • @Rch The largest number a double can handle is 1.79769e+308, which corresponds to roughly 2^1024. Usinglong long as an exponent is more than enough to exceed DOUBLE_MAX as it is least 64 bits. The resulting value is however limited to 18446744073709551615 (and will overflow a few times over). If you want to use the same type of implementation as you had (without using member functions) unfortunately that is the largest you will be able to get until you can use C++11 and constexpr. The C++98 compliant member method version will cover your expected range with the mentioned caveat.
    – Emily L.
    May 25 at 18:34










  • Yes, that's what I meant. That bitshift acts as an exponent for only as long as the size of your integer allows it, so if your unsigned long long is 64 bits you can only go up to 2^63 with this, after which it should return 0.
    – Rch
    May 25 at 19:15










Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f195125%2fcompile-time-positive-and-negative-powers-of-two%23new-answer', 'question_page');

);

Post as a guest






























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
3
down vote



accepted










Your code is not C++98 compliant



Standards prior to C++11 only allows const integer static member initialisation (see here).



So unfortunately your code is not C++98 compliant and in fact does not compile. So technically your code is off-topic on this site. As it is not working as intended. Your code probably only compiles because you're using a compiler that has an extension to allow non integer const static members.



To get around this typically you would define a static method that returns the value. In fact, this is why std::numeric_limits<T>::max() is a function instead of a static member value. However these cannot be evaluated in a constant expression context. You need C++11 constexpr for that afaik.



General advice



Some general advice. Template parameters are compile time constants by definition. Any sensible compiler should do any necessary casts during compile time so you will not get a run-time penalty for using higher precision.



For this reason I advice that you use the largest types possible in your template to get the most precision at no additional cost. I.e. change float to double and int to long long.



You should consider wrapping the templates that are not intended for use by the user in a suitable namespace. Typically I use a nested namespace called detail.



Also identifiers in ALL_CAPS_WITH_UNDERSCORES are typically reserved for macros. So I advice against using this naming convention for your identifiers as it will confuse many readers.



Better implementation



If we restrict ourselves to the bits of the code that are C++98 compliant, then we can have a much easier implementation for the integer exponentiation.



template<unsigned long long exponent>
struct ipow_2
static const unsigned long long value = (1 << exponent);
;


If you want a C++98 compliant negative exponent you can use:



template<long long exponent>
struct pow_2
static double value()
if (exponent == 0)
return 1;

else if (exponent > 0)
double half = pow_2<exponent/2>::value();
return (1<<(exponent&1))*half*half;

return 1 / pow_2<-exponent>::value();
;
;


But be aware that the same caveat for non-integer const initialisation still applies so it's not hugely useful in TMP for example.



If we accept use C++11 you can simplify and generalize to:



// Tail call optimization will transform this into the equivalent 
// loop
constexpr int64_t ipow_p(int64_t base, int exp, int64_t ans = 1)
return exp < 1 ?
ans : ipow_p(base*base, exp/2, (exp % 2) ? ans*base : ans);


constexpr double ipow(int base, int exp)
return exp > 0 ? ipow_p(base, exp) : 1.0 / ipow_p(base, -exp);



This is usable in a constant (TMP) context thanks to the constexpr qualifier and At -O2 the call will be expanded to a constant.






share|improve this answer























  • Thank you, this explains a lot. I was very surprised to see that this code didn't compile on c++11, I assume it's because it didn't have the same extension. I'll have to figure out what exactly is allowed on the compiler I work with.
    – Rch
    May 25 at 16:20










  • Regarding your solution, I have an issue with the fact that it doesn't allow for exponents higher than the size of a long (which should be shorter than the maximum exponent of a double or even a float).
    – Rch
    May 25 at 16:23










  • @Rch "...didn't compile on C++11" The standard is irrelevant here, your code is not valid, standard C++ in any version of the language. I used clang to compile which doesn't have the extensions that GCC has. GCC should show the same behavior if you pass -Wall -Wextra -pedantic -Werror... well, at least recent versions.
    – Emily L.
    May 25 at 18:32










  • @Rch The largest number a double can handle is 1.79769e+308, which corresponds to roughly 2^1024. Usinglong long as an exponent is more than enough to exceed DOUBLE_MAX as it is least 64 bits. The resulting value is however limited to 18446744073709551615 (and will overflow a few times over). If you want to use the same type of implementation as you had (without using member functions) unfortunately that is the largest you will be able to get until you can use C++11 and constexpr. The C++98 compliant member method version will cover your expected range with the mentioned caveat.
    – Emily L.
    May 25 at 18:34










  • Yes, that's what I meant. That bitshift acts as an exponent for only as long as the size of your integer allows it, so if your unsigned long long is 64 bits you can only go up to 2^63 with this, after which it should return 0.
    – Rch
    May 25 at 19:15














up vote
3
down vote



accepted










Your code is not C++98 compliant



Standards prior to C++11 only allows const integer static member initialisation (see here).



So unfortunately your code is not C++98 compliant and in fact does not compile. So technically your code is off-topic on this site. As it is not working as intended. Your code probably only compiles because you're using a compiler that has an extension to allow non integer const static members.



To get around this typically you would define a static method that returns the value. In fact, this is why std::numeric_limits<T>::max() is a function instead of a static member value. However these cannot be evaluated in a constant expression context. You need C++11 constexpr for that afaik.



General advice



Some general advice. Template parameters are compile time constants by definition. Any sensible compiler should do any necessary casts during compile time so you will not get a run-time penalty for using higher precision.



For this reason I advice that you use the largest types possible in your template to get the most precision at no additional cost. I.e. change float to double and int to long long.



You should consider wrapping the templates that are not intended for use by the user in a suitable namespace. Typically I use a nested namespace called detail.



Also identifiers in ALL_CAPS_WITH_UNDERSCORES are typically reserved for macros. So I advice against using this naming convention for your identifiers as it will confuse many readers.



Better implementation



If we restrict ourselves to the bits of the code that are C++98 compliant, then we can have a much easier implementation for the integer exponentiation.



template<unsigned long long exponent>
struct ipow_2
static const unsigned long long value = (1 << exponent);
;


If you want a C++98 compliant negative exponent you can use:



template<long long exponent>
struct pow_2
static double value()
if (exponent == 0)
return 1;

else if (exponent > 0)
double half = pow_2<exponent/2>::value();
return (1<<(exponent&1))*half*half;

return 1 / pow_2<-exponent>::value();
;
;


But be aware that the same caveat for non-integer const initialisation still applies so it's not hugely useful in TMP for example.



If we accept use C++11 you can simplify and generalize to:



// Tail call optimization will transform this into the equivalent 
// loop
constexpr int64_t ipow_p(int64_t base, int exp, int64_t ans = 1)
return exp < 1 ?
ans : ipow_p(base*base, exp/2, (exp % 2) ? ans*base : ans);


constexpr double ipow(int base, int exp)
return exp > 0 ? ipow_p(base, exp) : 1.0 / ipow_p(base, -exp);



This is usable in a constant (TMP) context thanks to the constexpr qualifier and At -O2 the call will be expanded to a constant.






share|improve this answer























  • Thank you, this explains a lot. I was very surprised to see that this code didn't compile on c++11, I assume it's because it didn't have the same extension. I'll have to figure out what exactly is allowed on the compiler I work with.
    – Rch
    May 25 at 16:20










  • Regarding your solution, I have an issue with the fact that it doesn't allow for exponents higher than the size of a long (which should be shorter than the maximum exponent of a double or even a float).
    – Rch
    May 25 at 16:23










  • @Rch "...didn't compile on C++11" The standard is irrelevant here, your code is not valid, standard C++ in any version of the language. I used clang to compile which doesn't have the extensions that GCC has. GCC should show the same behavior if you pass -Wall -Wextra -pedantic -Werror... well, at least recent versions.
    – Emily L.
    May 25 at 18:32










  • @Rch The largest number a double can handle is 1.79769e+308, which corresponds to roughly 2^1024. Usinglong long as an exponent is more than enough to exceed DOUBLE_MAX as it is least 64 bits. The resulting value is however limited to 18446744073709551615 (and will overflow a few times over). If you want to use the same type of implementation as you had (without using member functions) unfortunately that is the largest you will be able to get until you can use C++11 and constexpr. The C++98 compliant member method version will cover your expected range with the mentioned caveat.
    – Emily L.
    May 25 at 18:34










  • Yes, that's what I meant. That bitshift acts as an exponent for only as long as the size of your integer allows it, so if your unsigned long long is 64 bits you can only go up to 2^63 with this, after which it should return 0.
    – Rch
    May 25 at 19:15












up vote
3
down vote



accepted







up vote
3
down vote



accepted






Your code is not C++98 compliant



Standards prior to C++11 only allows const integer static member initialisation (see here).



So unfortunately your code is not C++98 compliant and in fact does not compile. So technically your code is off-topic on this site. As it is not working as intended. Your code probably only compiles because you're using a compiler that has an extension to allow non integer const static members.



To get around this typically you would define a static method that returns the value. In fact, this is why std::numeric_limits<T>::max() is a function instead of a static member value. However these cannot be evaluated in a constant expression context. You need C++11 constexpr for that afaik.



General advice



Some general advice. Template parameters are compile time constants by definition. Any sensible compiler should do any necessary casts during compile time so you will not get a run-time penalty for using higher precision.



For this reason I advice that you use the largest types possible in your template to get the most precision at no additional cost. I.e. change float to double and int to long long.



You should consider wrapping the templates that are not intended for use by the user in a suitable namespace. Typically I use a nested namespace called detail.



Also identifiers in ALL_CAPS_WITH_UNDERSCORES are typically reserved for macros. So I advice against using this naming convention for your identifiers as it will confuse many readers.



Better implementation



If we restrict ourselves to the bits of the code that are C++98 compliant, then we can have a much easier implementation for the integer exponentiation.



template<unsigned long long exponent>
struct ipow_2
static const unsigned long long value = (1 << exponent);
;


If you want a C++98 compliant negative exponent you can use:



template<long long exponent>
struct pow_2
static double value()
if (exponent == 0)
return 1;

else if (exponent > 0)
double half = pow_2<exponent/2>::value();
return (1<<(exponent&1))*half*half;

return 1 / pow_2<-exponent>::value();
;
;


But be aware that the same caveat for non-integer const initialisation still applies so it's not hugely useful in TMP for example.



If we accept use C++11 you can simplify and generalize to:



// Tail call optimization will transform this into the equivalent 
// loop
constexpr int64_t ipow_p(int64_t base, int exp, int64_t ans = 1)
return exp < 1 ?
ans : ipow_p(base*base, exp/2, (exp % 2) ? ans*base : ans);


constexpr double ipow(int base, int exp)
return exp > 0 ? ipow_p(base, exp) : 1.0 / ipow_p(base, -exp);



This is usable in a constant (TMP) context thanks to the constexpr qualifier and At -O2 the call will be expanded to a constant.






share|improve this answer















Your code is not C++98 compliant



Standards prior to C++11 only allows const integer static member initialisation (see here).



So unfortunately your code is not C++98 compliant and in fact does not compile. So technically your code is off-topic on this site. As it is not working as intended. Your code probably only compiles because you're using a compiler that has an extension to allow non integer const static members.



To get around this typically you would define a static method that returns the value. In fact, this is why std::numeric_limits<T>::max() is a function instead of a static member value. However these cannot be evaluated in a constant expression context. You need C++11 constexpr for that afaik.



General advice



Some general advice. Template parameters are compile time constants by definition. Any sensible compiler should do any necessary casts during compile time so you will not get a run-time penalty for using higher precision.



For this reason I advice that you use the largest types possible in your template to get the most precision at no additional cost. I.e. change float to double and int to long long.



You should consider wrapping the templates that are not intended for use by the user in a suitable namespace. Typically I use a nested namespace called detail.



Also identifiers in ALL_CAPS_WITH_UNDERSCORES are typically reserved for macros. So I advice against using this naming convention for your identifiers as it will confuse many readers.



Better implementation



If we restrict ourselves to the bits of the code that are C++98 compliant, then we can have a much easier implementation for the integer exponentiation.



template<unsigned long long exponent>
struct ipow_2
static const unsigned long long value = (1 << exponent);
;


If you want a C++98 compliant negative exponent you can use:



template<long long exponent>
struct pow_2
static double value()
if (exponent == 0)
return 1;

else if (exponent > 0)
double half = pow_2<exponent/2>::value();
return (1<<(exponent&1))*half*half;

return 1 / pow_2<-exponent>::value();
;
;


But be aware that the same caveat for non-integer const initialisation still applies so it's not hugely useful in TMP for example.



If we accept use C++11 you can simplify and generalize to:



// Tail call optimization will transform this into the equivalent 
// loop
constexpr int64_t ipow_p(int64_t base, int exp, int64_t ans = 1)
return exp < 1 ?
ans : ipow_p(base*base, exp/2, (exp % 2) ? ans*base : ans);


constexpr double ipow(int base, int exp)
return exp > 0 ? ipow_p(base, exp) : 1.0 / ipow_p(base, -exp);



This is usable in a constant (TMP) context thanks to the constexpr qualifier and At -O2 the call will be expanded to a constant.







share|improve this answer















share|improve this answer



share|improve this answer








edited May 25 at 21:05


























answered May 25 at 13:23









Emily L.

11k12372




11k12372











  • Thank you, this explains a lot. I was very surprised to see that this code didn't compile on c++11, I assume it's because it didn't have the same extension. I'll have to figure out what exactly is allowed on the compiler I work with.
    – Rch
    May 25 at 16:20










  • Regarding your solution, I have an issue with the fact that it doesn't allow for exponents higher than the size of a long (which should be shorter than the maximum exponent of a double or even a float).
    – Rch
    May 25 at 16:23










  • @Rch "...didn't compile on C++11" The standard is irrelevant here, your code is not valid, standard C++ in any version of the language. I used clang to compile which doesn't have the extensions that GCC has. GCC should show the same behavior if you pass -Wall -Wextra -pedantic -Werror... well, at least recent versions.
    – Emily L.
    May 25 at 18:32










  • @Rch The largest number a double can handle is 1.79769e+308, which corresponds to roughly 2^1024. Usinglong long as an exponent is more than enough to exceed DOUBLE_MAX as it is least 64 bits. The resulting value is however limited to 18446744073709551615 (and will overflow a few times over). If you want to use the same type of implementation as you had (without using member functions) unfortunately that is the largest you will be able to get until you can use C++11 and constexpr. The C++98 compliant member method version will cover your expected range with the mentioned caveat.
    – Emily L.
    May 25 at 18:34










  • Yes, that's what I meant. That bitshift acts as an exponent for only as long as the size of your integer allows it, so if your unsigned long long is 64 bits you can only go up to 2^63 with this, after which it should return 0.
    – Rch
    May 25 at 19:15
















  • Thank you, this explains a lot. I was very surprised to see that this code didn't compile on c++11, I assume it's because it didn't have the same extension. I'll have to figure out what exactly is allowed on the compiler I work with.
    – Rch
    May 25 at 16:20










  • Regarding your solution, I have an issue with the fact that it doesn't allow for exponents higher than the size of a long (which should be shorter than the maximum exponent of a double or even a float).
    – Rch
    May 25 at 16:23










  • @Rch "...didn't compile on C++11" The standard is irrelevant here, your code is not valid, standard C++ in any version of the language. I used clang to compile which doesn't have the extensions that GCC has. GCC should show the same behavior if you pass -Wall -Wextra -pedantic -Werror... well, at least recent versions.
    – Emily L.
    May 25 at 18:32










  • @Rch The largest number a double can handle is 1.79769e+308, which corresponds to roughly 2^1024. Usinglong long as an exponent is more than enough to exceed DOUBLE_MAX as it is least 64 bits. The resulting value is however limited to 18446744073709551615 (and will overflow a few times over). If you want to use the same type of implementation as you had (without using member functions) unfortunately that is the largest you will be able to get until you can use C++11 and constexpr. The C++98 compliant member method version will cover your expected range with the mentioned caveat.
    – Emily L.
    May 25 at 18:34










  • Yes, that's what I meant. That bitshift acts as an exponent for only as long as the size of your integer allows it, so if your unsigned long long is 64 bits you can only go up to 2^63 with this, after which it should return 0.
    – Rch
    May 25 at 19:15















Thank you, this explains a lot. I was very surprised to see that this code didn't compile on c++11, I assume it's because it didn't have the same extension. I'll have to figure out what exactly is allowed on the compiler I work with.
– Rch
May 25 at 16:20




Thank you, this explains a lot. I was very surprised to see that this code didn't compile on c++11, I assume it's because it didn't have the same extension. I'll have to figure out what exactly is allowed on the compiler I work with.
– Rch
May 25 at 16:20












Regarding your solution, I have an issue with the fact that it doesn't allow for exponents higher than the size of a long (which should be shorter than the maximum exponent of a double or even a float).
– Rch
May 25 at 16:23




Regarding your solution, I have an issue with the fact that it doesn't allow for exponents higher than the size of a long (which should be shorter than the maximum exponent of a double or even a float).
– Rch
May 25 at 16:23












@Rch "...didn't compile on C++11" The standard is irrelevant here, your code is not valid, standard C++ in any version of the language. I used clang to compile which doesn't have the extensions that GCC has. GCC should show the same behavior if you pass -Wall -Wextra -pedantic -Werror... well, at least recent versions.
– Emily L.
May 25 at 18:32




@Rch "...didn't compile on C++11" The standard is irrelevant here, your code is not valid, standard C++ in any version of the language. I used clang to compile which doesn't have the extensions that GCC has. GCC should show the same behavior if you pass -Wall -Wextra -pedantic -Werror... well, at least recent versions.
– Emily L.
May 25 at 18:32












@Rch The largest number a double can handle is 1.79769e+308, which corresponds to roughly 2^1024. Usinglong long as an exponent is more than enough to exceed DOUBLE_MAX as it is least 64 bits. The resulting value is however limited to 18446744073709551615 (and will overflow a few times over). If you want to use the same type of implementation as you had (without using member functions) unfortunately that is the largest you will be able to get until you can use C++11 and constexpr. The C++98 compliant member method version will cover your expected range with the mentioned caveat.
– Emily L.
May 25 at 18:34




@Rch The largest number a double can handle is 1.79769e+308, which corresponds to roughly 2^1024. Usinglong long as an exponent is more than enough to exceed DOUBLE_MAX as it is least 64 bits. The resulting value is however limited to 18446744073709551615 (and will overflow a few times over). If you want to use the same type of implementation as you had (without using member functions) unfortunately that is the largest you will be able to get until you can use C++11 and constexpr. The C++98 compliant member method version will cover your expected range with the mentioned caveat.
– Emily L.
May 25 at 18:34












Yes, that's what I meant. That bitshift acts as an exponent for only as long as the size of your integer allows it, so if your unsigned long long is 64 bits you can only go up to 2^63 with this, after which it should return 0.
– Rch
May 25 at 19:15




Yes, that's what I meant. That bitshift acts as an exponent for only as long as the size of your integer allows it, so if your unsigned long long is 64 bits you can only go up to 2^63 with this, after which it should return 0.
– Rch
May 25 at 19:15












 

draft saved


draft discarded


























 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f195125%2fcompile-time-positive-and-negative-powers-of-two%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

Greedy Best First Search implementation in Rust

Function to Return a JSON Like Objects Using VBA Collections and Arrays

C++11 CLH Lock Implementation