|
Boost : |
From: Gennaro Prota (gennaro_prota_at_[hidden])
Date: 2003-01-22 12:31:36
I think we had a lot of communication problems in this thread. But
this post should definitely solve the case :-) First of all, I was not
criticizing your algorithm! It's the best one I know of (in terms of
maximum number of passes) and extremely simple to understand; also its
"runtime version"
if (m /= 2)
if (x >> m)
return m + log2(x >> m, m);
else
return log2(x, m);
else
return 0;
is one of the fastest I know, on a variety of implementations. Tests
show that its templated incarnation is fast to compile too. Thus, far
from me saying that it is not a good strategy to calculate the
logarithm (and you will have a confirmation below, because I'm
proposing it in place of the current implementation). It just has a
peculiarity which can be a (little) problem when you don't have a
working <limits> header: i.e. it requires you to know in advance how
many bits there are, at most, in your number. As I hinted at, when you
have the <limits> header that's no problem at all: you start from
m = ( 1 + std::numeric_limits<unsigned long>::digits) / 2;
and you have efficient, elegant and perfectly portable code. So far so
good.
For boost, you know that we don't have always a working <limits>
header. Now I proposed a few days ago a simplification of our messed
up implementation of boost/detail/limits.hpp based on the idea that
numeric_limits::digits is *computable* from the maximum value of the
corresponding type; e.g.:
1 + static_log2<UINT_MAX> ::value
is the value of digits for unsigned int. Of course in this case log2
must not use in turn numeric_limits :-) Well, on the subject of simple
implementations, I also proposed adding some toys like these:
template <typename T>
struct max_of { /* not defined */ };
template <>
struct max_of<unsigned char> {
static const int value = UCHAR_MAX;
};
template <>
struct max_of<unsigned short> {
static const int value = SHRT_MAX;
};
....
....
with which you can get rid of all the stuff that passes __imin,
__imax, etc. through the base class in the implementation of
/detail/limits.hpp. Besides helping implementing numeric_limits, these
min_of and max_of are also IMHO what should be in the integer library
in place of the integer_traits (which derives from numeric_limits for
no reason). That said I propose, as a first pass, to replace the
current static_log2.hpp with this:
----------- code ---------------------
// Boost integer/static_log2.hpp header file
-------------------------------//
// (C) Copyright Daryle Walker 2001. Permission to copy, use,
modify, sell and
// distribute this software is granted provided this copyright notice
appears
// in all copies. This software is provided "as is" without express
or
// implied warranty, and with no claim as to its suitability for any
purpose.
// See http://www.boost.org for updates, documentation, and revision
history.
#ifndef BOOST_INTEGER_STATIC_LOG2_HPP
#define BOOST_INTEGER_STATIC_LOG2_HPP
#include "boost/config.hpp"
// The implementation of log2(x) we use here requires knowing
// in advance the number of value bits in typeof(x). This info
// is obtainable through std::numeric_limits::digits and the
// resulting code is perfectly portable across conforming C++
// implementations. In cases where we don't have a working
// <limits> header instead we "compute" the number of value bits
// by means of
// CHAR_BIT * sizeof(x);
// which is ok only if x doesn't have unused bits in its object
// representation (which is the case for all non conforming
// implementations supported by boost)
#ifdef BOOST_NO_LIMITS
# include <climits> // for CHAR_BIT
#else
# include <limits>
#endif
namespace boost {
namespace detail {
#ifdef BOOST_NO_LIMITS
// this must be changed if the broken C++ implementation (which
// doesn't provide a correct <limits> header) has padding bits
// on unsigned long
static const int width_of_unsigned_long = CHAR_BIT * sizeof(1UL);
#else
static const int width_of_unsigned_long =
std::numeric_limits<unsigned long>::digits;
#endif
// The algorithm is
//
// if (m /= 2)
// if (x >> m)
// return m + log2(x >> m, m);
// else
// return log2(x, m);
// else
// return 0;
//
// with computation of m/2 "anticipated"
// respect to template instantiation
//
template <unsigned long x, int m = (1 +
detail::width_of_unsigned_long)/2>
struct static_log2_impl {
enum { c = (x >> m) > 0 }; // x >= 2**m
enum { value = c*m + static_log2_impl< (x >> (c*m)), m/2
>::value };
};
template <>
struct static_log2_impl<1,0> { enum { value = 0 }; };
} // namespace detail
// static_log2
//
template <unsigned long x>
struct static_log2 { enum { value =
detail::static_log2_impl<x>::value }; };
template <>
struct static_log2 <1> { enum { value = 0 }; };
template <>
struct static_log2 <0> { }; // log2(0) is undefined
} // namespace boost
#endif // include guard
---------- end code --------------------------
Here are the results obtained with your shell script on my machine:
g++ 2.95
current:
real 0m58.069s
user 0m33.250s
sys 0m20.568s
new:
real 0m43.960s
user 0m18.760s
sys 0m20.335s
g++ 3.2
current:
real 0m53.832s
user 0m25.031s
sys 0m24.484s
new:
real 0m39.284s
user 0m14.240s
sys 0m23.280s
What do you think?
Genny.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk