Template Versions of cmath Functions?

Started by
6 comments, last by Antheus 15 years, 9 months ago
I'm doing a bit of math in my program and I'm getting a bit frustrated with the math functions defined in the cmath header. Why? Because each function offers only two flavors: float and double. The problem is, I'm doing a lot of integer math, including exponentiation and logarithms that I know are always in the integer domain. I have to constantly cast and recast values over and over again for the compiler to deduce which overload to use. What's worse, the values I'm working with become floating point numbers, which introduces inaccuracies and doesn't allow me to do simple comparisons (instead, I have to sometimes resort to epsilon values, which are basically a threshold I compare to the difference). This is very annoying. I'm wondering if there exists a template based library that exposes the same standard functions, such as std::pow and std::log. Really, I just need versions of said functions that work with ints. I understand that the writing int based versions of these functions won't always make sense (because the domain and range in pure mathematics is real numbers), but I have no need for the fractional parts and therefore really don't want to pay for it (with inaccuracy and comparison woes). Is it even possible to implement them with arbitrary types? While I'm at it, are there versions of std::log for any base other than e and 10? I've been using this hack so far.
float r = std::log(x) / std::log(2)
This (I think) yields the log base 2 of x. Messy, eh? Not only that, but I didn't bother with the use of static_cast<> here. Also, this obviously isn't ideal since I'm taking the natural logarithm of two numbers! I've seen hints online of a std::log2 function, and haven't tried to use it, but I've also read online that it doesn't exist (i.e., isn't standard). So... any input? :-) In any case, suggestions for using these functions with integers is appreciated. Thanks!
Advertisement
Quote:Original post by GenuineXP

including exponentiation and logarithms that I know are always in the integer domain.


How do you do that in integer domain?

e^4 * log(314) / log(2), for example...

What is it that you're doing?
Sorry if I'm not using the term domain correctly. What I mean to say is that I know ahead of time that the results of the functions I'm using will be integers (I guess this is the range, not domain; I also know that the parameters will also be integers though). This is because I'm strictly working in terms of integer powers of two.

This is also why I'm interested in a function to do a base two logarithm. The base is always two, and the parameters are always powers of two, so I shouldn't have any fractional parts in the result.

Please, someone correct me if I've said something silly here. :-)

In any case, it's the casting I'd like to avoid. I've been able to get rid of any floating point comparisons... by casting. I'm worried that one of these casts will be inaccurate now if the floating point input is too far off. Dunno, maybe I'm worrying too much about inaccuracy. I'm dealing with pretty small numbers most of the time.
Quote:Original post by GenuineXP
Sorry if I'm not using the term domain correctly. What I mean to say is that I know ahead of time that the results of the functions I'm using will be integers (I guess this is the range, not domain; I also know that the parameters will also be integers though). This is because I'm strictly working in terms of integer powers of two.


Why are logical shift operators not sufficient?
I believe Dave Eberly's book on game engine design has a templated math library.
Quote:Original post by Antheus
Quote:Original post by GenuineXP
Sorry if I'm not using the term domain correctly. What I mean to say is that I know ahead of time that the results of the functions I'm using will be integers (I guess this is the range, not domain; I also know that the parameters will also be integers though). This is because I'm strictly working in terms of integer powers of two.


Why are logical shift operators not sufficient?

...

Wow, I'm stupid. :-)

But seriously, thanks. I hadn't even thought about that for some reason. That ought to clean up a lot of my code.

I could use this.
int r = 1ul << x;

I'm thinking this will assign 2^x to r. Am I right?

Also, how could I use shifts to find the base two logarithm? Is there a way?

Thanks again.
Quote:Original post by GenuineXP
While I'm at it, are there versions of std::log for any base other than e and 10? I've been using this hack so far.
float r = std::log(x) / std::log(2)

This (I think) yields the log base 2 of x. Messy, eh? Not only that, but I didn't bother with the use of static_cast<> here. Also, this obviously isn't ideal since I'm taking the natural logarithm of two numbers!

I've seen hints online of a std::log2 function, and haven't tried to use it, but I've also read online that it doesn't exist (i.e., isn't standard).


That's not a "hack"; it's a legit way of doing logs. Most computers/calculators/etc with math functionality don't come with anything but base 10 and base e logarithms because you can just do what you're doing now and divide by the logarithm of the base you want. If you're so intent on having a log2 function, make your own that wraps up what you're doing there.

As for using int-specific functions, you could wrap the float and/or double functions into int functions of your own. Then you don't need to cast in your actual code.
Quote:Original post by tendifo

As for using int-specific functions, you could wrap the float and/or double functions into int functions of your own.


Far from it.

The emphasis seems to be on base-2 calculations.

Left and right shift perform most of operations.
BSR and BSF (bitscan reverse/forward) would likely be the by far best way to perform logarithms.
AND can be used for modulo.
There's also a bunch of derived formulas for many other calculations which don't exactly fit into 2^n form. Alas, there's little need for them these days, and I've forgotten the details.

One problem with this type of operations is overflow and underflow, as well as various rounding problems. As such, it's quite difficult to write a generic library.

However, knowing particular domain and type of operations, writing a wrapper around these operations, in C++ even through overloading the operators, or even going the template way, would allow for very efficient implementation.

But as said, this depends on actual operations involved. If everything can really be expressed using 2^n values, then logical operations will be lightning fast.

This topic is closed to new replies.

Advertisement