473,394 Members | 2,048 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,394 software developers and data experts.

Critique requested.....

Hello,

Could you please critique this code please? It's for creating sine/cosine/tan
ratio lookup tables. Any ideas on how to improve it in terms of efficiency, any
problems forseen in its use, etc?

I've ommitted various header includes and comments that I thought wouldn't do
any good to list by the way.

I realise now that the name 'BinLookup' would perhaps have been more meaningful
than 'BiLookup'...
========= 222 LINES OF CODE FOLLOW - BE WARNED ========

/** Compare d1 to d2, using error as max difference between d1 and d2
for which both are equal. */
template<typename T>
bool
compare_eq(const T& d1, const T& d2, const T& error)
{
return std::abs((d1 > d2) ? d1-d2 : d2-d1) <= error;
}

/** Compare d1 to d2, using error as min difference between d1 and d2
for which both are not equal. */
template<typename T>
bool
compare_neq(const T& d1, const T& d2, const T& error)
{
return std::abs((d1 > d2) ? d1-d2 : d2-d1) > error;
}
/**Generates and contains trigonometric ratio lookup tables (sin, cos,
tan).

Q: "Why don't you make them static, so only one copy of them can
possibly exist?"

A: "Some platforms have different memory locations for different
things (e.g. ROM and RAM areas on consoles.) Doing it this way
allows these tables to exist in different memory locations at the
choice of the programmer. For example, they might always exist in
ROM, and only sometimes in RAM depending on the game mode that is
active.

NOTE: According to the gcc manual page (and confirmed by my own
frustrating experience), the x86 and the Motorola 68000 keep more
precision for doubles in registers they do in memory. So comparing
a looked up value to a non-looked up value with '==' is just asking
for trouble.
*/
template<typename T, uint_fast32_t units>
class Trig_Tables
{
public:
class Lookup;

class BiLookup;

/** Initialise the static triglookup tables. */
Trig_Tables()
{
init_tables_();
}

/** Because It Should Be There (tm) */
virtual
~Trig_Tables()
{
}

/** Converts the given positive argument (units in a circle)
into radians. Not optimised, so only useful for
initialisation! Note: shouldn't go over the limit (won't
be wrapped around.) */
static inline T
to_rads(uint_fast32_t arg);

/// The sin table
T sin_[units];

/// The cos table
T cos_[units];

/// The tan table
T tan_[units];

/// initialise the tables
void
init_tables_()
{
for(uint_fast32_t i = 0; i < units; ++i) {
sin_[i] = std::sin(to_rads(i));
cos_[i] = std::cos(to_rads(i));
tan_[i] = std::tan(to_rads(i));
}
}

}; // class Trig_Tables

template<typename T, uint_fast32_t units>
inline T
Trig_Tables<T, units>::to_rads(uint_fast32_t arg)
{
return static_cast<T>(arg)/static_cast<T>(units)
* static_cast<T>(2.0*M_PI);
}

/** Provides generic support for getting trig table values from a
Trig_Tables object. */
template<typename T, uint_fast32_t units>
class Trig_Tables<T, units>::Lookup
{
public:
Lookup(const Trig_Tables<T, units>& arg)
: tables_(arg)
{
}

/** Gets sin value from table WITHOUT a bounds check. */
T
sin(uint_fast32_t arg) const
{
return tables_.sin_[arg];
}

/** Gets sin value after truncating the argument to the
smallest value that measures the same angle (i.e. if
units is 360 (degrees), then if 361 is provided, actual
value gotten from sin table is sin(1). Also absolutes the
value (i.e. sin(-7) becomes -sin(7).) */
T
get_sin(int_fast32_t arg) const
{
return (arg < 0)
? -tables_.sin_[std::abs(arg)%units]
: tables_.sin_[std::abs(arg)%units];
}

/** Gets cos value from table WITHOUT a bounds check. */
T
cos(uint_fast32_t arg) const
{
return tables_.cos_[arg];
}

/** Gets cos value as get_sin (see above). Note: As cos(x) is
the same as cos(-x), this eliminates an extra check.
Groovy. eh?

@see get_sin. */
T
get_cos(int_fast32_t arg) const
{
return tables_.cos_[std::abs(arg)%units];
}

/** Gets tan value from table WITHOUT a bounds check. */
T
tan(uint_fast32_t arg) const
{
return tables_.tan_[arg];
}

/** Gets tan value as get_sin (see above).

@see get_sin. */
T
get_tan(int_fast32_t arg) const
{
return (arg < 0)
? -tables_.tan_[std::abs(arg)%units]
: tables_.tan_[std::abs(arg)%units];
}

protected:
const Trig_Tables<T, units>& tables_;

}; // class Trig_Tables::Lookup

/** Only for use with Trig_Tables whose units a power of 2, where x is
an unsigned integer. If this is NOT the case, throws a
std::logic_error, and the object is NOT created.

Provides faster access to the get_* functions of Lookup, by using
'&' to wrap the requested angles around.

What is 'wrap around'? Wrap-around is changing angle 361 to 1,
which both give the correct results. */
template<typename T, uint_fast32_t units>
class Trig_Tables<T, units>::BiLookup : public Trig_Tables<T, units>::Lookup
{
public:
/** Simply uses Lookup's default constructor with a check for
binary angle wrap-around compatibility.

@throw std::logic_error */
BiLookup(const Trig_Tables<T, units>& arg)
: Lookup(arg), mask_(units-1)
{
if( ((units&(units-1)) != 0) || units < 2)
throw std::logic_error("BiLookup used for unit "
"size not a power of 2");
}

/** Uses '&' instead of '%' to wrap around values. */
T
get_sin(int_fast32_t arg) const
{
return (arg < 0)
? -this->tables_.sin_[arg&mask_]
:this-> tables_.sin_[arg&mask_];
}

/** Uses '&' instead of '%' to wrap around values. */
T
get_cos(int_fast32_t arg) const
{
return this->tables_.cos_[arg&mask_];
}

/** Uses '&' instead of '%' to wrap around values. */
T
get_tan(int_fast32_t arg) const
{
return (arg < 0)
? -this->tables_.tan_[arg&mask_]
: this->tables_.tan_[arg&mask_];
}

protected:
const uint_fast32_t mask_;

}; // class Trig_Tables::BiLookup

========= END OF CODE ========

--
Entry in RollerCoaster Tycoon 2 readme.txt file:
RollerCoaster Tycoon2 must be played on a video card capable of 640x480 screen
resolution at a bit depth setting of 256 bits.
And the proof that playing too many strategy games causes loss of humour:
http://tinyurl.com/dyrtt
Jan 25 '06 #1
5 1835

Asfand Yar Qazi wrote:
Hello,

Could you please critique this code please? It's for creating sine/cosine/tan
ratio lookup tables. Any ideas on how to improve it in terms of efficiency, any
problems forseen in its use, etc?

I've ommitted various header includes and comments that I thought wouldn't do
any good to list by the way.

I realise now that the name 'BinLookup' would perhaps have been more meaningful
than 'BiLookup'...
========= 222 LINES OF CODE FOLLOW - BE WARNED ========

/** Compare d1 to d2, using error as max difference between d1 and d2
for which both are equal. */
template<typename T>
bool
compare_eq(const T& d1, const T& d2, const T& error)
{
return std::abs((d1 > d2) ? d1-d2 : d2-d1) <= error;
}
/** Compare d1 to d2, using error as min difference between d1 and d2
for which both are not equal. */
template<typename T>
bool
compare_neq(const T& d1, const T& d2, const T& error)
{
return std::abs((d1 > d2) ? d1-d2 : d2-d1) > error;
}
but std::abs is not a template function, it takes an int parameter and
returns an int. (Did you want to use fabs?).

Q: "Why don't you make them static, so only one copy of them can
possibly exist?"

(better answer): because a static lookup table uses a lot of memory (if
it's a decently sized one anyway) and you might be using it for only
part of the program. This way gives you the flexibility of getting rid
of it when you no longer need it.
*/
what is a uint_fast32_t ? A 32-bit unsigned type?
template<typename T, uint_fast32_t units>
class Trig_Tables
{
public:
class Lookup;

class BiLookup;
I've never previously seen nested classes only declared iwthin a class
then defined outside. (Is this allowed? Does it being a template make
it any different?)
/** Initialise the static triglookup tables. */
Trig_Tables()
{
init_tables_();
}

/** Because It Should Be There (tm) */
virtual
~Trig_Tables()
{
}
No it shouldn't be there unless you are planning to derive from it. I
don't see any other virtual functions so it's likely you won't be. You
might wish to disable copying and assignment though, because you have
rather a big class here and an accidental copy/assign would not be
cheap.
/** Converts the given positive argument (units in a circle)
into radians. Not optimised, so only useful for
initialisation!
So why make it public if it should only be called in initialisation?
static inline T
to_rads(uint_fast32_t arg); /// The sin table
T sin_[units];

/// The cos table
T cos_[units];

/// The tan table
T tan_[units];

/// initialise the tables do you really need these pointless comments?

What I don't see is a comment as to what the units are exactly. How do
I find the sin of a number? Do I have to convert a value myself then
look up in a table, and I note these tables are public.

And do I interpoloate (linearly) between two values or simply pick the
closest match (given I have to pass in an integral value).
void
init_tables_()
{
for(uint_fast32_t i = 0; i < units; ++i) {
sin_[i] = std::sin(to_rads(i));
cos_[i] = std::cos(to_rads(i));
tan_[i] = std::tan(to_rads(i));
}
so it's not even optimised but you call to_rads 3 times for each loop.
why not
T rads = to_rads(i);
sin_i[i] = std::sin( rads) ;
cos_i[i] = std::cos( rads );
tain_i[i] = std::tan( rads );

}

}; // class Trig_Tables

template<typename T, uint_fast32_t units>
inline T
Trig_Tables<T, units>::to_rads(uint_fast32_t arg)
{
return static_cast<T>(arg)/static_cast<T>(units)
* static_cast<T>(2.0*M_PI);
}
2.0* M_PI (is that a standard I don't know of or does it only exist on
some compilers) is const so hopefully the compiler can optimise it as
one. units is also const within the function so effectively it's arg*K
where K is a const. So effectively our loop above could simply have
done rads += K at each iteration with rads=0.0 as its initial value.

It's also noticeable you are caching the entire cycle of the sin table
when you need to cache only 1/4 of it.
/** Provides generic support for getting trig table values from a
Trig_Tables object. */
why are they classes though? And why does BiLookup "inherit" from
Lookup? (Lookup has no virtual methods nor a virtual destructor.
Instead it just overloads the functions).
template<typename T, uint_fast32_t units>
class Trig_Tables<T, units>::Lookup
{
public:
Lookup(const Trig_Tables<T, units>& arg)
: tables_(arg)
{
}

/** Gets sin value after truncating the argument to the
smallest value that measures the same angle (i.e. if
units is 360 (degrees), then if 361 is provided, actual
value gotten from sin table is sin(1). Also absolutes the
value (i.e. sin(-7) becomes -sin(7).) */
T
get_sin(int_fast32_t arg) const
{
return (arg < 0)
? -tables_.sin_[std::abs(arg)%units]
: tables_.sin_[std::abs(arg)%units];
}
(non virtual function )
<sniip> }; // class Trig_Tables::Lookup

template<typename T, uint_fast32_t units>
class Trig_Tables<T, units>::BiLookup : public Trig_Tables<T, units>::Lookup
{
public:
/** Simply uses Lookup's default constructor with a check for
binary angle wrap-around compatibility.

@throw std::logic_error */
BiLookup(const Trig_Tables<T, units>& arg)
: Lookup(arg), mask_(units-1)
{
if( ((units&(units-1)) != 0) || units < 2)
throw std::logic_error("BiLookup used for unit "
"size not a power of 2");
}

/** Uses '&' instead of '%' to wrap around values. */
T
get_sin(int_fast32_t arg) const
{
return (arg < 0)
? -this->tables_.sin_[arg&mask_]
:this-> tables_.sin_[arg&mask_];
}

}; // class Trig_Tables::BiLookup


Ask yourself:

Will T ever be anything other than double?

Wouldn't access functions be better?

Are you happy with the loss of accuracy. (Presumably these are being
used for graphics in a heavy-use performance-critical situation).

Jan 25 '06 #2
Earl Purple <ea********@gmail.com> wrote:

Asfand Yar Qazi wrote:
/** Compare d1 to d2, using error as min difference between d1 and d2
for which both are not equal. */
template<typename T>
bool
compare_neq(const T& d1, const T& d2, const T& error)
{
return std::abs((d1 > d2) ? d1-d2 : d2-d1) > error;
}


but std::abs is not a template function, it takes an int parameter and
returns an int. (Did you want to use fabs?).


In _TC++PL:SE_, Section 22.3 (p.660):

[talking about <cmath> and <math.h>]

double abs(double); // absolute value; not in C, same as fabs()

In addition, <cmath> and <math.h> supply these functions for float
and long double arguments.

True that it is not a template though.

--
Marcus Kwok
Jan 25 '06 #3

Earl Purple wrote:

[snip]
template<typename T, uint_fast32_t units>
class Trig_Tables
{
public:
class Lookup;

class BiLookup;

I've never previously seen nested classes only declared within a class
then defined outside. Is this allowed?
Yes.
Does it being a template make
it any different?


No, other than having to be careful to get the template syntax right.

Best regards,

Tom

Jan 25 '06 #4
Earl Purple wrote:

<snip>
Q: "Why don't you make them static, so only one copy of them can
possibly exist?"
(better answer): because a static lookup table uses a lot of memory (if
it's a decently sized one anyway) and you might be using it for only
part of the program. This way gives you the flexibility of getting rid
of it when you no longer need it.


Ah yes, that as well, good point about being able to get rid of it.

You see, on the Gameboy Advance for example, there are several types of memory
that can be used. There is EWRAM (256K 16-bit memory external to the processor
- "normal" speed) and there is IWRAM (32K 32-bit memory EMBEDDED onto the
processor). Code can be run from either type of memory, but its not so quick to
access one type of memory from the other. So, if some code's on one type of
memory, its better to have to lookup tables on the same type of memory.


what is a uint_fast32_t ? A 32-bit unsigned type?
The fastest integer type for the given machine that has at least 32 bits of
precision (for example on x86-64, it would be a 64-bit int).


No it shouldn't be there unless you are planning to derive from it. I
don't see any other virtual functions so it's likely you won't be. You
might wish to disable copying and assignment though, because you have
rather a big class here and an accidental copy/assign would not be
cheap.
I just thought, in case in the future at some point I want to do it, but yeah I
suppose it would be pointless.

/** Converts the given positive argument (units in a circle)
into radians. Not optimised, so only useful for
initialisation!

So why make it public if it should only be called in initialisation?


Someone else might want to use it as a utility function.

static inline T
to_rads(uint_fast32_t arg);
/// The sin table
T sin_[units];

/// The cos table
T cos_[units];

/// The tan table
T tan_[units];

/// initialise the tables


do you really need these pointless comments?


no :-)

What I don't see is a comment as to what the units are exactly. How do
I find the sin of a number? Do I have to convert a value myself then
look up in a table, and I note these tables are public.
Ah, that's 'cos the Lookup objects that access it have those comments instead.

And do I interpoloate (linearly) between two values or simply pick the
closest match (given I have to pass in an integral value).
No, the 'precision' is supplied at creation time - so for example, I might
supply a 'units' template argument of 720, in which case looking up a sine value
of '1' would give me the sine of 0.5.

As you noted later on, this is for speed-critical low-precision maths use, so I
expect to use a 'units' argument of 200 most of the time and then use BinLookup
for the 'wrap-around with &' effect.

void
init_tables_()
{
for(uint_fast32_t i = 0; i < units; ++i) {
sin_[i] = std::sin(to_rads(i));
cos_[i] = std::cos(to_rads(i));
tan_[i] = std::tan(to_rads(i));
}

so it's not even optimised but you call to_rads 3 times for each loop.
why not
T rads = to_rads(i);
sin_i[i] = std::sin( rads) ;
cos_i[i] = std::cos( rads );
tain_i[i] = std::tan( rads );


Thought the compiler would do it for me.


}

}; // class Trig_Tables

template<typename T, uint_fast32_t units>
inline T
Trig_Tables<T, units>::to_rads(uint_fast32_t arg)
{
return static_cast<T>(arg)/static_cast<T>(units)
* static_cast<T>(2.0*M_PI);
}

2.0* M_PI (is that a standard I don't know of or does it only exist on
some compilers) is const so hopefully the compiler can optimise it as
one. units is also const within the function so effectively it's arg*K
where K is a const. So effectively our loop above could simply have
done rads += K at each iteration with rads=0.0 as its initial value.


As you said, compiler does it for us...

It's also noticeable you are caching the entire cycle of the sin table
when you need to cache only 1/4 of it.
Since I want this largely targeted towards speed, I'd rather cache the whole
table than use extra instructions at lookup time to get the correct result.

/** Provides generic support for getting trig table values from a
Trig_Tables object. */

why are they classes though? And why does BiLookup "inherit" from
Lookup? (Lookup has no virtual methods nor a virtual destructor.
Instead it just overloads the functions).


So that it the sin() cos() and tan() functions are inherited from it - they just
return the cached value directly.

get_XXX() does a check and wraps the angles provided around, a task that the
BinLookup's overriding functions do more efficiently using just a '&' operation
rather than a more expensive std::abs + % operator combination.


Ask yourself:

Will T ever be anything other than double?
It could be a float, which on the GBA and perhaps other platforms, takes up less
space than double and is more efficient (on a i686 at least, double out-performs
float.)

It could be a custom fixed point integer type with overriden sin/cos/tan
functions which get used when caching the trig ratios.

Wouldn't access functions be better?
I can't see why...

Are you happy with the loss of accuracy. (Presumably these are being
used for graphics in a heavy-use performance-critical situation).


Yes, you guessed correctly. The loss of accuracy is expected and part of the
design.
Much thanks to all those who contributed - I will gladly take all points into
consideration.

Thanks,
Asfand Yar
--
Entry in RollerCoaster Tycoon 2 readme.txt file:
RollerCoaster Tycoon2 must be played on a video card capable of 640x480 screen
resolution at a bit depth setting of 256 bits.
And the proof that playing too many strategy games causes loss of humour:
http://tinyurl.com/dyrtt
Jan 25 '06 #5
Marcus Kwok wrote:
Earl Purple <ea********@gmail.com> wrote:
Asfand Yar Qazi wrote:
/** Compare d1 to d2, using error as min difference between d1 and d2
for which both are not equal. */
template<typename T>
bool
compare_neq(const T& d1, const T& d2, const T& error)
{
return std::abs((d1 > d2) ? d1-d2 : d2-d1) > error;
}

but std::abs is not a template function, it takes an int parameter and
returns an int. (Did you want to use fabs?).


In _TC++PL:SE_, Section 22.3 (p.660):

[talking about <cmath> and <math.h>]

double abs(double); // absolute value; not in C, same as fabs()

In addition, <cmath> and <math.h> supply these functions for float
and long double arguments.

True that it is not a template though.

Except for valarray (Standard 26.3.3.3)

In addition,
Overload of abs in <cstdlib> for long is defined in 26.5/4.
Overloads of abs in <cmath> for float, double, and long double are
defined in 26.5/6.
Jan 25 '06 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

7
by: Cynthia Turcotte | last post by:
Hi all -- A client has hired me to, among other things, optimize her web site for search engine submission. So being the dutiful SEO geek that I am, I went through and optimized each and every...
19
by: TC | last post by:
Are there any good sites or forums for a web critique? I went to alt.html.critique and it's pretty dead.
9
by: bowsayge | last post by:
Inspired by fb, Bowsayge decided to write a decimal integer to binary string converter. Perhaps some of the experienced C programmers here can critique it. It allocates probably way too much...
34
by: Andrew Clark | last post by:
Hello all, Wow, has it ever been a long time since I posted anything to this group. I don't keep as current as I used to anymore; I only lurk from time to time when I have a spare moment. I am...
188
by: christopher diggins | last post by:
I have posted a C# critique at http://www.heron-language.com/c-sharp-critique.html. To summarize I bring up the following issues : - unsafe code - attributes - garbage collection -...
39
by: Eric | last post by:
There is a VB.NET critique on the following page: http://www.vb7-critique.741.com/ for those who are interested. Feel free to take a look and share your thoughts. Cheers, Eric. Ps: for those...
17
by: Joe | last post by:
I'm a long-time lurker, so I know what to expect! Can someone please look at this and make appropriate comments? http://members.aardvark.net.au/grakat/temp/ It's only four pages, and it should...
61
by: warint | last post by:
My lecturer gave us an assignment. He has a very "mature" way of teaching in that he doesn't care whether people show up, whether they do the assignments, or whether they copy other people's work....
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.