Jump to content
  • Advertisement
Sign in to follow this  
rozz666

Templated math

This topic is 3887 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to make a simple math library (sqrt, pow, log, etc. ), but with templates instead of functions. E.g. const int a = 50; const int b = 2; const int c = log<a, b>::value; Now, I think I know how to do it with integers, but I is there a way to use floats? Floats can't be used as template parameters. They could be packed (casted) to ints or long longs (except long double), passed as parameters and unpacked, but is there an elegant way to cast bits of a float immediate value to int or long long? The same applies for unpacking, but this may be less elegant, since it will be hidden in the implementation. Anyways, supposing this problem is solved, the compiler might report too deep template recursion for floats, right?

Share this post


Link to post
Share on other sites
Advertisement
I have no idea what you are trying to do, maybe you could elaborate on that.

Casting the float "bits" to int "bits" can be done with a construct like that:

float f = 47.11f;
int& i = *((int*)&f);

And the other way round;

float& fr = *((float*)&i);

However, I don't see how this would help your problem.

Share this post


Link to post
Share on other sites
Quote:
Original post by Rattenhirn
I have no idea what you are trying to do, maybe you could elaborate on that.


It's template metaprogramming. I want the calculations to be done during compile time.

Quote:

Casting the float "bits" to int "bits" can be done with a construct like that:

float f = 47.11f;
int& i = *((int*)&f);

And the other way round;

float& fr = *((float*)&i);

However, I don't see how this would help your problem.


It won't :-) I need to cast immediate values like:

int i = something(4.7f); // i should contains bits of 4.7f NOT 4

Share this post


Link to post
Share on other sites
You could use a fixed-point representation (which means you really used integers). As you noted, floats cannot be non-type template arguments, so you're really out of luck there. Fixed-point is likely the best option you have.

Share this post


Link to post
Share on other sites
Unfortunately you cannot pack floats into ints without loss of information. That's why casting can be a dangerous thing to do. The computer representation of floats and ints is like comparing a slide rule and an abacus. The first uses logarithmic representation of decimal numbers, the second just counts numbers. They cannot be interchanged. Non-type template parameters are severely restricted at this point in time.

--random

Share this post


Link to post
Share on other sites
Quote:
Original post by random_thinker
Unfortunately you cannot pack floats into ints without loss of information. That's why casting can be a dangerous thing to do. The computer representation of floats and ints is like comparing a slide rule and an abacus. The first uses logarithmic representation of decimal numbers, the second just counts numbers. They cannot be interchanged. Non-type template parameters are severely restricted at this point in time.

--random


Assuming certain conditions, int and float both have 4 bytes. So there can't be a loss of information. I need to sth like *(int *) &4.7f

Share this post


Link to post
Share on other sites
You can't do it with casting, because reinterpreting an int as a float or vice-versa would require a reinterpret_cast (hence the name), which means operating on something in memory, which means doing the work at runtime (incompatible with the template, and defeating the purpose even if it could work). Template recursion won't be a problem unless you actually recurse :)

It would have to be done with some kind of macro I guess. Something like (making very non-portable assumptions of course):


#define F2I(x) /* you're on your own for this one. ;) */
#define I2F(x) /* similarly */
// Won't be able to call a function or look up in a table there - has to be
// known compile-time-evaluable.


template <int encoded_base, int encoded_operand>
class log { static const float value; };

template <int encoded_base, int encoded_operand>
const float log::value = std::log(I2F(encoded_base), I2F(encoded_operand));

const float log2of50 = log<F2I(2), F2I(50)>::value;

Share this post


Link to post
Share on other sites
OK...I stand corrected. Would something like this help:

Quote:

A C Solution

Fortunately, the 1999 ISO C Standard defines two functions which were not a part of earlier versions of the standard. These functions round doubles and floats to long ints and have the following function prototypes:

long int lrint (double x) ;
long int lrintf (float x) ;


These functions are defined in <math.h> but are only usable with the GNU C compiler if C99 extensions have been enabled before <math.h> is included. This is done as follows:

#define _ISOC9X_SOURCE 1
#define _ISOC99_SOURCE 1

#include <math.h>


Two versions of the defines ensure that the required functions are picked up with older header files. In the GLIBC (the standard version of the C library on Linux) header files, these functions are defined as inline functions and are in fact inlined by gcc (the standard C compiler on Linux) when optimisation is switched on. If optimisation is switched off, the functions are not inlined and an executable calling these functions will need to be linked with the maths library.


Edit---

There's a good thread already on this subject here. Apparently SiCrane indicated that MSVC6 allows double and float as non type parameters and most C++ compilers will allow references to const double.

Hope it's of use...

--random

[Edited by - random_thinker on October 2, 2007 1:05:12 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman
You can't do it with casting, because reinterpreting an int as a float or vice-versa would require a reinterpret_cast (hence the name), which means operating on something in memory, which means doing the work at runtime (incompatible with the template, and defeating the purpose even if it could work). Template recursion won't be a problem unless you actually recurse :)

It would have to be done with some kind of macro I guess. Something like (making very non-portable assumptions of course):


#define F2I(x) /* you're on your own for this one. ;) */
#define I2F(x) /* similarly */
// Won't be able to call a function or look up in a table there - has to be
// known compile-time-evaluable.


template <int encoded_base, int encoded_operand>
class log { static const float value; };

template <int encoded_base, int encoded_operand>
const float log::value = std::log(I2F(encoded_base), I2F(encoded_operand));

const float log2of50 = log<F2I(2), F2I(50)>::value;



Well, that's actually what I DON'T want to do. You are using std::log which gets computed at runtime.

Now, I (probably, haven't done yet) know how do write a log template, like:


template <int number, int base>
struct log {
static const int value = /* .... */;
};


The difference with your example is tht I can use it everywhere a constant is needed, e.g. array declaration:


char digits[log<pow<2, sizeof(unsigned) * CHAR_BITS>::value, 10>::value + 1];



As for floats, theoretically it is possible to do all the calculations using emulated FP arithmetic, so it's definitely possible to use floats in templates.

Because of lack of other options, maybe use:


template <int mantissa, int exp = 0>
struct tfloat {
static const int value = /* some bit shifts etc. according to IEEE 754 */;
};


The use:


const float x = logf<tfloat<5, 2>, tfloat<3> >;

Share this post


Link to post
Share on other sites
Non-integral non-template type parameters can be used with pointers or references to non-integral objects with external linkage. Example:

template <const float & theta>
struct Sin {
static float sin() {
return theta - theta * theta * theta / 6;
}
};

extern const float theta = 0.7853981633975f;
extern const float sin_theta = Sin<theta>::sin();

Produces:

_TEXT ENDS
PUBLIC ?sin_theta@@3MB ; sin_theta
_DATA SEGMENT
?sin_theta@@3MB DD 03f34641er ; 0.704653 ; sin_theta
_DATA ENDS
END

In MSVC 7.1 in a release build.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!