views:

192

answers:

5

I need to store a collection of ints and doubles (representing nominal and real valued data) in c++. I could obviously store them all in a std::vector<double> , but this feels a bit wrong and doesn't get the aesthetics bonus points.

I could also cook up something based on polymorphism, but I also need the collection to be really efficient: both storing and retrieving the data in the collection should be as fast as possible. I find it hard to judge whether such a solution would be maximally efficient.

I also found boost::variant, which might be of help here.

Additional info: the number of items in the collection will be small (<100) and known when initializing the collection.

Summarizing: I could obviously solve this in countless ways, but I am unsure what would be a good solution when (i) efficiency is really important and (ii) I also want to write somewhat nice code. What is my best bet here?

Edit, additional info: The collection represents a 'row' in a larger data set, its elements represent the values of certain 'columns'. The properties of the rows are known, so it is known what kind of data is stored at which position. The 'efficiency' I am talking about is primarily the efficiency of retrieving the int/double value of a certain column, although fast setting of values is important too. I have some functions that operate on the data that need to retrieve it as fast as possible. Example:

typedef std::vector<double> Row;

void doubleFun(Row const &row)
{
    // Function knows there's always a double at index 0
    double value = row[0];
    ...
}

void integerFun(Row const &row)
{
    // Function knows there's always an integer at index 1
    int value = row[1];
    ...
}

After some more thought and reading the suggestions so far, it seems that just storing int columns and double columns in two separate vectors is a solid solution. The collection Row could then just define two different members for retrieving nominal and real data that the functions can use.

Just storing as a vector<double> is okay too I guess, but it depends on how fast the conversion between double and int is (which is probably pretty impressive).

Sorry for being a little unclear at first, I hope it's clearer and now and that I can get some more thoughts on the matter.

+2  A: 

You could use a union type and use that in your vector. But in that case you'd have to have some way to know which elements of the vector should be treated as ints and which ones should be treated as doubles. To keep track of which ones are ints and which ones are doubles you could use a bitset or something like that.

I'm not sure if your goal is to avoid heavy floating point calculations. If it is then the bitset may be more efficient. If not, and exact int precision isn't important, then you might as well just store them all as doubles.

#include <vector>
#include <bitset>

union di
{
    double d;
    int i;
};


int main(int argc, char* argv[])
{

    std::bitset<2> bitsetInts;

    std::vector<di> v;
    di e1;
    e1.d = 3.9;
    v.push_back(e1);

    di e2;
    e2.i = 3;
    bitsetInts.set(1);
    v.push_back(e2);

    return 0;
}
Brian R. Bondy
this is great if the max size is fixed, exactly what I was thinking.
Tom
+4  A: 

Is ordering an important point in your container ?

If not so:

class MyContainer
{
    std::vector<double> doubles;
    std::vector<int>    ints;

    push(double value) { doubles.push_back(value); }
    push(int value)    { ints.push_back(value); }

   ....
};

The iterator part (to browse the whole container) could be a little trickier...

yves Baumes
Given the updated requirements, this looks most sensible.
Tom
+1  A: 

There is the boost tuple, which you can use if you know the types at compile time. But if the number of items is small, efficiently in wasting 100 bytes shouldn't be a concern.

pythonic metaphor
+2  A: 

I would go for the boost::variant solution, it perfectly fits your needs.

TimW
+4  A: 

Why not using directly a vector of double? Since integers can be converted to doubles without loss of precision... it looks to me the simplest and most efficient solution.

What remains to be set (and I couldn't figure out from your question) how can you make the difference between normal and real values. The issue remains open in any solution you might choose.

Cătălin Pitiș
true, now is 10.0 a double or an integer?
gbjbaanb
The real question is "Is 10.0 a normal or a real value?". But, as I said in my answer... I don't have enough information. The same question can be asked in case of using variants.
Cătălin Pitiș
@Cătălin A boost::variant is type save, you can use a visitor to retrieve the correct type
TimW
@TimW: Is it efficient? If there is another way of making the difference between normal and real values... why should I bother using variants. Again... we know nothing about possibilities to make a difference.
Cătălin Pitiș
I am wary of conversions between int and double, which, it seems, can introduce problems. For example int (1) is translated to 0.9999 ... But I didn't read "What every computer scientist should know about floating-Point Arithmetic". Does someone on SO knows plz ? (I am interested in the answer !! :-) )
yves Baumes
0 and all powers of 2 in the double range are represented exactly. 0.999... instead is not represented exactly, for any number of 9s. There are integer numbers in the double range that can't be represented as doubles at all (see http://en.wikipedia.org/wiki/Unit_in_the_last_place), but you only have to worry about that if you use 64-bit ints.
Dan Berindei