2

Ok so I have a my Trig class that I use to store a static table of values for faster execution of sin, cos, and tan functions in my program. Are there any comments and/or speed improvements that could be made over my current method? Thanks to previous answers, I am already feeling much more competent with C++.

Trig.h

#pragma once
#include <math.h>

class Trig
{
private:
    struct Table
    {
        static const int multiple = 10; // Accurately stores values to 1/10 of a degree
        double sin[360*multiple];
        Table();
    };
    static const Table table;
    static void shrinkRange(double*); // Shrinks degrees to range 0-359 for proper array indexing
public:
    static double sin(double);
    static double cos(double);
    static double tan(double);
};

Trig.cpp

#include "Trig.h"

Trig::Table::Table() // table constructor
{
    double const PI = 3.14159265358979323;
    double const degToRad = PI/180.0;
    double const incr = 1.0/multiple;
    int index = 0;
    for (double angle = 0; index != 360*table.multiple; angle += incr)
        Table::sin[index++] = _INC_MATH::sin(angle*degToRad);
}

Trig::Table const Trig::table; // initialize static table member

void Trig::shrinkRange(double* degrees)
{
    if (*degrees >= 360)
        *degrees -= 360*( (int)*degrees/360);
    if (*degrees < 0)
        *degrees += 360*( -(int)*degrees/360 + 1);
}

double Trig::sin(double degrees)
{
    shrinkRange(&degrees);
    degrees *= table.multiple;
    return Trig::table.sin[(int)(degrees+0.5)];
}

double Trig::cos(double degrees)
{
    return Trig::sin(degrees + 90);
}

double Trig::tan(double degrees)
{
    return Trig::sin(degrees)/Trig::cos(degrees);
}
  • 2
    This isn't Java. It's `Trig::createTable`. – Luchian Grigore Aug 07 '13 at 09:35
  • 4
    This is basic C++ syntax. I recommend getting [a good book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). (try `Trig::createTable()`) – R. Martinho Fernandes Aug 07 '13 at 09:36
  • There's no point in editing an existing closed off-topic question to the extent that it bears no relation to the original question in terms of topic, etc. As such, you probably want to ask a new question. – John Parker Aug 09 '13 at 14:23

5 Answers5

2

C++ isn't Java. In this case, you cannot call a function or access a member on a class, because there are no class objects; you can access a static member simply by specifying its scope:

Trig::createTable();
Trig::COS_TABLE[120];

Also (but this is true in Java as well), you can automatically ensure the proper initialization by using dynamic initialization. If you really want to keep the current structure, you could add something like:

bool initted = (Trig::createTable(), true);

any where at namespace scope. More idiomatic would be to define an object type to contain the two tables, with a constructor which initialized them, and declare a static instance of this:

class Trig
{
public:
    struct Tables
    {
        double sin[360];
        double cos[360];
        Tables();
    };
    static Tables const tables;
    //  ...
};

Trig::Tables const Trig::tables;

Trig::Tables::Tables()
{
    double rad = PI / 180.0;
    for ( int angle = 0; angle != 360; ++ angle ) {
        sin[angle] = std::sin( angle * rad );
        cos[angle] = std::cos( angle * rad );
    }
}

No need to explicitly call Trig::createTable; the compiler takes care of this for you.

(Similar techniques are available in Java. For some reason, they are not often used, where as the above is very idiomatic C++.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
0
Trig::createTable();
double x = Trig::COS_Table[120];
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Zagatho
  • 523
  • 1
  • 6
  • 22
  • 2
    Welcome to Stack Overflow. Please read the [About] page soon. Note that when answering questions, it is important to be careful that your answer is syntactically correct. It is also necessary to add a little explanation. As corrected (by adding the `];` after `120`), your code is accurate, but you should mention the changes (`::` instead of `.` twice, and adding `()`) and briefly explain why they're necessary. – Jonathan Leffler Aug 07 '13 at 09:51
0

In order to access a static you need to use :: i.e.

Trig::COS_TABLE

rather than Trig.COS_TABLE

However, a class that just contains statics is a tad odd in C++.

doctorlove
  • 18,872
  • 2
  • 46
  • 62
  • Not really. Traits are widely used to customize templates. (In this case, of course, I rather expect that the class contains a lot more, and that he's simply cut the parts which weren't causing him a problem. Otherwise, yes: this would not be idiomatic. Except that you'd probably not make the members static, but declare a static instance, and use the cosntructor to do the initialization.) – James Kanze Aug 07 '13 at 09:52
  • @JamesKanze of course you are right, it just smells like a Javaism here. – doctorlove Aug 07 '13 at 10:00
  • Might be worth mentioning that :: is an operator and is called the scope resolution operator. – Paul Renton Aug 07 '13 at 20:08
0

Static variable needs to be accessed using namespcae operator :: unlike . used in Java.

So, to call method createTable() without creating an object, use this syntax:

Trig::createTable();

As SIN_TABLE and COS_TABLE table are also static, use syntax as:

Trig::SIN_TABLE[120];
Trig::COS_TABLE[120];
toro2k
  • 19,020
  • 7
  • 64
  • 71
PratAm
  • 33
  • 1
  • 7
0

Best, make your tables (non-static) members of a class for which you have a single static object:

#include <cmath>                         // include C++ header instead of C

namespace trig {                         // use a namespace instead of a class
  const double pi = 4*std::atan2(1.,1.); // avoid pre-processor macros
  namespace details {                    // hide details in inner namespace
    struct sin_cos_tables                // class holding tables
    {
      double SIN[360];
      double COS[360];
      sin_cos_tables()                   // default constructor: set tables
      {
        const double fac = pi/180;
        for(int angle = 0; angle != 360; ++angle)
        {
          SIN[angle] = std::sin(angle*fac);
          COS[angle] = std::cos(angle*fac);
        }
      }
    };
    static sin_cos_tables TABLES;        // static object constructed at start-up
  }
  static const double*SIN_TABLE = details::TABLES.SIN;  // for your convenience
  static const double*COS_TABLE = details::TABLES.COS;  // (not really necessary)
}

int main()
{
  double x = trig::SIN_TABLE[120];
}

this way, the tables are automatically created before your other (non-static) code runs.

Walter
  • 44,150
  • 20
  • 113
  • 196
  • This definetly seems like a much nicer solution. I've never defined a namespace before so I'm gonna have to look into that a little but I see what you mean with the static object. Might I ask why I should avoid using the pre-processor macro to define PI? In what situations would a macro be prefered over a const? – Corpsecreate Aug 07 '13 at 10:12
  • see http://stackoverflow.com/questions/9941107/what-are-the-major-advantages-of-const-versus-define-for-global-constants – Walter Aug 07 '13 at 10:18
  • Re the comment "avoid pre-processor macros": it is far preferable to use a pre-processor macro here than to enter the constant yourself, possibly making a typo, or not entering enough digits for the platform in question. (On the other hand, all caps usually means a macro, so the variables shouldn't be `SIN` and `COS`.) And I don't understand the point of the extra static pointers. – James Kanze Aug 07 '13 at 11:24
  • @user2660086 If you expect to have instances, use a class. If you don't a namespace is more idiomatic. – James Kanze Aug 07 '13 at 11:29
  • @JamesKanze The extra static pointers are only for convenience to make the body of `main` as similar to the original as possible. – Walter Aug 07 '13 at 12:59
  • @JamesKanze I disagree regarding the macro. If this is a header file, which may be included elsewhere, then `#define` should be avoided, as it will contaminate the name space of all files #including the header (directly or indirectly) and it may clash with an existing macro #defined elsewhere in files #included. – Walter Aug 07 '13 at 13:02
  • @Walter It should be in a (platform) standard header. Both Posix and Windows extend `` to support `M_PI`, which is what he probably should be using. Otherwise: I'd missed the fact that he defined `PI` himself. I agree that this is wrong, and that `static double const` is far better. But it still shouldn't be in his class; he needs _one_ definition for all classes and all users. (And I wonder if your declaration doesn't introduce an order of initialization issue. I would certainly use a floating point literal to define it, and not some function call.) – James Kanze Aug 07 '13 at 14:49