views:

110

answers:

3

I need to convert time from one format to another in C++ and it must be cross-platform compatible. I have created a structure as my time container. The structure fields must also be unsigned int as specified by legacy code.

struct time{   
unsigned int timeInteger;   
unsigned int timeFraction;
} time1, time2;

Mathematically the conversion is as follows:

time2.timeInteger = time1.timeInteger + 2208988800

time2.timeFraction = (time1.timeFraction * 20e-6) * 2e32

Here is my original code in C++ however when I attempt to write to a binary file, the converted time does not match with the truth data. I think this problem is due to a type casting mistake? This code will compile in VS2008 and will execute.

void convertTime(){
   time2.timeInteger  = unsigned int(time1.timeInteger + 2209032000);
   time2.timeFraction = unsigned int(double(time1.timeFraction) * double(20e-6)*double(pow(double(2),32)));
}
+1  A: 

Slightly unrelated, I think you should rethink your type design. You are basically talking about two different types here. They happen to store the same data, albeit in different results.

To minimize errors in their usage, you should define them as two completely distinct types that have a well-defined conversion between them.

Consider for example:

struct old_time {
    unsigned int timeInteger;   
    unsigned int timeFraction;
};

struct new_time {
public:
    new_time(unsigned int ti, unsigned int tf) :
        timeInteger(ti), timeFraction(tf) { }

    new_time(new_time const& other) :
        timeInteger(other.timeInteger),
        timeFraction(other.timeFraction) { }

    new_time(old_time const& other) : 
        timeInteger(other.timeInteger + 2209032000U),
        timeFraction(other.timeFraction * conversion_factor) { }

    operator old_time() const {
        old_time other;
        other.timeInteger = timeInteger - 2209032000U;
        other.timeFraction = timeFraction / conversion_factor;
        return other;
    }

private:
    unsigned int timeInteger;   
    unsigned int timeFraction;
};

(EDIT: of course this code doesn’t work for the reasons pointed out below.

Now this code can be used frictionless in a safe way:

time_old told; /* initialize … */

time_new tnew = told; // converts old to new format
time_old back = tnew; // … and back.
Konrad Rudolph
I think I will follow your advice and define them as distinct types, but this doesn't help the casting problem because in the above code the conversion factor create the integer overflow. But please don't delete the above code, it is very useful
Elpezmuerto
@Elpezmuerto: yes, I’m sorry I couldn’t help with the original problem … at the moment I’m having trouble wrapping my head around this since the conversion factor clearly doesn’t fit into an `unsigned int`.
Konrad Rudolph
@Konrad: I think the goal is to properly handle the integer overflow which in C and C++ will result in "wrapping around." Maybe I can create a separate variable for all the zeros associated with 2^32 and break up timeFraction into `char` or `short` and write to my binary file in pieces rather than one complete `unsigned int`?
Elpezmuerto
+1  A: 

Just a guess, but are you assuming that 2e32 == 2^32? This assumption would make sense if you're trying to scale the result into a 32 bit integer. In fact 2e32 == 2 * 10^32

Mark B
Good catch. Thanks!
Elpezmuerto
A: 

The problem is that (20 ^ -6) * (2 e32) is far bigger than UINT_MAX. Maybe you meant 2 to the power of 32, or UINT_MAX, rather than 2e32.

In addition, your first line with the integer, the initial value must be less than (2^32 - 2209032000), and depending on what this is measured in, it could wrap round too. In my opinion, set the first value to be a long long (normally 64bits) and change 2e32.

If you can't change the type, then it may become necessary to store the field as it's result in a double, say, and then cast to unsigned int before use.

DeadMG