Can a C++ union perform math on the number it contains? - c++

Is it possible in C++ to create a union that would let me do something like this ...
union myTime {
long millis;
double seconds;
};
BUT, have it somehow do the conversion so that if I input times in milliseconds, and then call seconds, it will take the number and divide it by 1000, or conversely, if I input the number in seconds, then call millis, it would multiply the number by 1000...
So that:
myTime.millis = 1340;
double s = myTime.seconds;
Where s would equal 1.34
or
myTime.seconds = 2.5;
long m = myTime.millis;
Where m would = 2500
Is this possible?

A union is just different representations for the same value (the same bytes), so you can't define any smart logic over that.
In this case, you can define a class with conversion functions (both for initializtion or for getting the data).
class myTime {
public:
myTime(long millis);
double as_seconds();
static void from_seconds(double seconds);
};
Notice that as mentioned in other answers, for time conversions you can use std::chrono objects (c++11 and above)

To answer the question as asked: No. Unions are lower-level structure that simply allow multiple object representations to live in the same memory space. In your example, long and double share the same address.
They are not, however, smart enough to automatically do a conversation of any kind. Accessing the inactive member of a union is actually undefined behavior in most cases (there are exceptions for if you have a common-initial sequence in a standard-layout object).
Even if the behavior were well-defined, the value you would see in the double would be the double interpretation of the byte-pattern necessary to represent 1340.
If your problem is specifically to do with converting millis to seconds, as per your example, have you considered using std::chrono::duration units? These units are designed specifically for automatically doing these conversions between time units for you -- and you are capable of defining durations with custom representations (such as double).
Your example in your problem could be rewritten:
using double_seconds = std::chrono::duration<double>;
const auto millis = std::chrono::millis{1340};
const auto m = double_seconds{millis};
// m contains 1.340

You can if you abuse the type system a bit:
union myTime {
double seconds;
class milli_t {
double seconds;
public:
milli_t &operator=(double ms) {
seconds = ms/1000.0;
return *this; }
operator double() const { return seconds * 1000; }
} millis;
};
Now if you do
myTime t;
t.millis = 1340;
double s = t.seconds;
s would equal 1.34
and
myTime t;
t.seconds = 2.5;
long m = t.millis;
m would be 2500, exactly as you desire.
Of course, why you would want to do this is unclear.

Related

How can I make this expression involving floating-point functions a compile-time constant?

I have a constant integer, steps, which is calculated using the floor function of the quotient of two other constant variables. However, when I attempt to use this as the length of an array, visual studio tells me it must be a constant value and the current value cannot be used as a constant. How do I make this a "true" constant that can be used as an array length? Is the floor function the problem, and is there an alternative I could use?
const int simlength = 3.154*pow(10,7);
const float timestep = 100;
const int steps = floor(simlength / timestep);
struct body bodies[bcount];
struct body {
string name;
double mass;
double position[2];
double velocity[2];
double radius;
double trace[2][steps];
};
It is not possible with the standard library's std::pow and std::floor function, because they are not constexpr-qualified.
You can probably replace std::pow with a hand-written implementation my_pow that is marked constexpr. Since you are just trying to take the power of integers, that shouldn't be too hard. If you are only using powers of 10, floating point literals may be written in the scientific notation as well, e.g. 1e7, which makes the pow call unnecessary.
The floor call is not needed since float/double to int conversion already does flooring implicitly. Or more correctly it truncates, which for positive non-negative values is equivalent to flooring.
Then you should also replace the const with constexpr in the variable declarations to make sure that the variables are usable in constant expressions:
constexpr int simlength = 3.154*my_pow(10,7); // or `3.154e7`
constexpr float timestep = 100;
constexpr int steps = simlength / timestep;
Theoretically only float requires this change, since there is a special exception for const integral types, but it seems more consistent this way.
Also, I have a feeling that there is something wrong with the types of your variables. A length and steps should not be determined by floating-point operations and types, but by integer types and operations alone. Floating-point operations are not exact and introduce errors relative to the mathematical precise calculations on the real numbers. It is easy to get unexpected off-by-one or worse errors this way.
You cannot define an array of a class type before defining the class.
Solution: Define body before defining bodies.
Furthermore, you cannot use undefined names.
Solution: Define bcount before using it as the size of the array.
Is the floor function the problem, and is there an alternative I could use?
std::floor is one problem. There's an easy solution: Don't use it. Converting a floating point number to integer performs similar operation implicitly (the behaviour is different in case of negative numbers).
std::pow is another problem. It cannot be replaced as trivially in general, but in this case we can use a floating point literal in scientific notation instead.
Lastly, non-constexpr floating point variable isn't compile time constant. Solution: Use constexpr.
Here is a working solution:
constexpr int simlength = 3.154e7;
constexpr float timestep = 100;
constexpr int steps = simlength / timestep;
P.S. trace is a very large array. I would recommend against using so large member variables, because it's easy for the user of the class to not notice such detail, and they are likely to create instances of the class in automatic storage. This is a problem because so large objects in automatic storage are prone to cause stack overflow errors. Using std::vector instead of an array is an easy solution. If you do use std::vector, then as a side effect the requirement of compile time constant size disappear and you will no longer have trouble using std::pow etc.
Because simlength is 3.154*10-to-the-7th, and because timestep is 10-squared, then the steps variable's value can be written as:
3.154e7 / 1e2 == 3.154e5
And, adding a type-cast, you should be able to write the array as:
double trace[2][(int)(3.154e5)];
Note that this is HIGHLY IRREGULAR, and should have extensive comments describing why you did this.
Try switching to constexpr:
constexpr int simlength = 3.154e7;
constexpr float timestep = 1e2;
constexpr int steps = simlength / timestep;
struct body {
string name;
double mass;
double position[2];
double velocity[2];
double radius;
double trace[2][steps];
};

Specialization of STL Templates

I am trying to write high-performance code that uses random numbers, using Mersenne Twister. It takes roughly ~5ns to generate a random unsigned long long. This is used to generate a double, however, these take ~40ns to generate in a distribution.
Viewing the STL code the doubles, generated by a distribution, are generated by calls to std::generate_canonical, which involves a std::ceil and std::log2 operation, I believe it is these that are costly.
These operations are unnecessary as they are used to calculate the number of bits needed for calls to any RNG implementation. As this is known before compile time, I have written my own implementation that does not make these calls, and the time to generate a double is ~15ns.
Is it possible to specialize a templated STL function? If so how is this achieved, my attempts so far result in the original function still being used. I would like to specialize this STL function as I would still like to use the distributions in <random>.
This is in Visual C++, though once the code has been developed it will be run on Linux and use either GCC or ICC. If the method for generating doubles on Linux is different, (and quicker), this problem is irrelevant.
Edit 1:
I believe all distributions requiring a double make calls to std::generate_canonical, this function creates a double in the range [0,1) and the correct precision is created by iteratively adding calls to the RNG operator(). The log2 and ceil are used to calculate the number of iterations.
MSVC std::generate_canonical
// FUNCTION TEMPLATE generate_canonical
template<class _Real,
size_t _Bits,
class _Gen>
_Real generate_canonical(_Gen& _Gx)
{ // build a floating-point value from random sequence
_RNG_REQUIRE_REALTYPE(generate_canonical, _Real);
const size_t _Digits = static_cast<size_t>(numeric_limits<_Real>::digits);
const size_t _Minbits = _Digits < _Bits ? _Digits : _Bits;
const _Real _Gxmin = static_cast<_Real>((_Gx.min)());
const _Real _Gxmax = static_cast<_Real>((_Gx.max)());
const _Real _Rx = (_Gxmax - _Gxmin) + static_cast<_Real>(1);
const int _Ceil = static_cast<int>(_STD ceil(
static_cast<_Real>(_Minbits) / _STD log2(_Rx)));
const int _Kx = _Ceil < 1 ? 1 : _Ceil;
_Real _Ans = static_cast<_Real>(0);
_Real _Factor = static_cast<_Real>(1);
for (int _Idx = 0; _Idx < _Kx; ++_Idx)
{ // add in another set of bits
_Ans += (static_cast<_Real>(_Gx()) - _Gxmin) * _Factor;
_Factor *= _Rx;
}
return (_Ans / _Factor);
}
My Simplified Version
template<size_t _Bits>
double generate_canonical(std::mt19937_64& _Gx)
{ // build a floating-point value from random sequence
const double _Gxmin = static_cast<double>((_Gx.min)());
const double _Gxmax = static_cast<double>((_Gx.max)());
const double _Rx = (_Gxmax - _Gxmin) + static_cast<double>(1);
double _Ans = (static_cast<double>(_Gx()) - _Gxmin);
return (_Ans / _Rx);
}
This function is written in namespace std {}
Edit 2:
I found a solution please see my answer below.
Sorry, specializing Standard Library functions is not allowed; doing so results in Undefined Behavior.
However, you can use alternative distributions; C++ has well-defined interfaces between generators and distributions.
Oh, and just to eliminate the possibility of a beginners error (since you don't show code): you do not create a new distribution for every number.
By creating a template function with all parameters set, and declaring the functions as inline, it is possible to create user defined version of std::generate_canonical.
User defined std::generate_canonical:
namespace std {
template<>
inline double generate_canonical<double, static_cast<size_t>(-1), std::mt19937>(std::mt19937& _Gx)
{ // build a floating-point value from random sequence
const double _Gxmin = static_cast<double>((_Gx.min)());
const double _Rx = (static_cast<double>((_Gx.max)()) - _Gxmin) + static_cast<double>(1);
double _Ans = (static_cast<double>(_Gx()) - _Gxmin);
_Ans += (static_cast<double>(_Gx()) - _Gxmin) *_Rx;
return (_Ans / _Rx * _Rx);
}
template<>
inline double generate_canonical<double, static_cast<size_t>(-1), std::mt19937_64>(std::mt19937_64& _Gx)
{ // build a floating-point value from random sequence
const double _Gxmin = static_cast<double>((_Gx.min)());
const double _Rx = (static_cast<double>((_Gx.max)()) - _Gxmin) + static_cast<double>(1);
return ((static_cast<double>(_Gx()) - _Gxmin) / _Rx);
}
}
The second parameter static_cast<size_t>(-1) should be modified to whatever value is used by specific libraries, this is the case for VC++ but may be different for GCC. This means it is not portable.
This function has been defined for std::mt19337 and std::mt19937_64 and seems to be used for STL distributions correctly.
Results:
double using std::generate_canonical
Generating 400000000 doubles using standard MT took: 17625 milliseconds
This equivalent to: 44.0636 nanoseconds per value
Generating 400000000 doubles using 64bit MT took: 11958 milliseconds
This equivalent to: 29.8967 nanoseconds per value
double using new generate_canonical
Generating 400000000 doubles using standard MT took: 4843 milliseconds
This equivalent to: 12.1097 nanoseconds per value
Generating 400000000 doubles using 64bit MT took: 2645 milliseconds
This equivalent to: 6.61362 nanoseconds per value

Cast chrono::milliseconds to uint64_t?

Assuming I have the number of milliseconds in variable x:
chrono::milliseconds x = std::chrono::duration_cast<chrono::milliseconds>(something);
how do I convert x from chrono::milliseconds to uint64_t?
I have tried:
uint64_t num = std::chrono::duration_cast<uint64_t>(x);
but it says:
no matching function for call to
duration_cast(std::chrono::milliseconds&)
First of all, you generally shouldn't do this sort of thing. <chrono> provides a type-safe and generic units library for handling time durations, and there are few good reasons to escape this safety and genericity.
Some examples of the ills that don't happen with a type-safe, generic units library and which do happen with type-unsafe integral types:
// a type-safe units library prevents these mistakes:
int seconds = ...
int microseconds = seconds * 1000; // whoops
int time = seconds + microseconds; // whoops
void bar(int seconds);
bar(microseconds); // whoops
// a generic duration type prevents the need for:
unsigned sleep(unsigned seconds);
int usleep(useconds_t useconds);
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
int attosleep(long long attoseconds); // ???
// just use:
template<typename Duration>
int sleep_for(Duration t); // users can specify sleep in terms of hours, seconds, microseconds, femetoseconds, whatever. Actual sleep duration depends on QoI, as always.
An example of a good reason would be compatibility with a third party library that made the unfortunate decision not to use a type-safe, generic units library in their API. In this case the conversions should be done as close as possible to the API boundary in order to minimize the extent to which unsafe types are used.
So with that said, when you do have a good reason, you do so like this:
std::chrono::milliseconds x = ...
std::uint64_t num = x.count();
Keep in mind that the predefined chrono durations such as chrono::milliseconds use signed representations, so you'll need to take care to ensure the value is appropriate for conversion to uint64_t.
The prototype for std::chrono::duration_cast is:
template <class ToDuration, class Rep, class Period>
constexpr ToDuration duration_cast(const duration<Rep,Period>& d);
You can't get an uint64_t directly, because it converts durations (duration_cast). So you need to create a std::duration with std::uint64_t.
using cast = std::chrono::duration<std::uint64_t>;
std::uint64_t ticks = std::chrono::duration_cast< cast >(something).count();

C++ chrono - get duration as float or long long

I have a duration
typedef std::chrono::high_resolution_clock Clock;
Clock::time_point beginTime;
Clock::time_point endTime;
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - beginTime);
And I get duration in std::chrono::milliseconds. But I need duration as float or long long. How to do that?
From the documentation
template<
class Rep,
class Period = std::ratio<1>
> class duration;
Class template std::chrono::duration represents a time interval. It
consists of a count of ticks of type Rep and a tick period, where the
tick period is a compile-time rational constant representing the
number of seconds from one tick to the next.
And:
count returns the count of ticks
So a duration stores a number of ticks of a specified period of time, and count will return that number using the underlying representation type. So if the duration's representation is long long, and the period is std::milli, then .count() will return a long long equal to the number of milliseconds represented by the duration.
In general you should avoid using weak types like float or long long to represent a duration. Instead you should stick with 'rich' types, such as std::chrono::milliseconds or an appropriate specialization of std::chrono::duration. These types aid correct usage and readability, and help prevent mistakes via type checking.
Underspecified / overly general:
– void increase_speed(double);
– Object obj; … obj.draw();
– Rectangle(int,int,int,int);
Better: – void increase_speed(Speed);
– Shape& s; … s.draw();
– Rectangle(Point top_left, Point bottom_right);
– Rectangle(Point top_left, Box_hw b);
— slide 18 from Bjarne's talk
std::chrono is "a consistent subset of a physical quantities library that handles only units of time and only those units of time with exponents equal to 0 and 1."
If you need to work with quantities of time you should take advantage of this library, or one that provides more complete unit systems, such as boost::units.
There are rare occasions where quantities must be degraded to weakly typed values. For example, when one must use an API that requires such types. Otherwise it should be avoided.
As float answer.
std::chrono's duration typedefs are integer. However, duration class can accept float.
See my duration typedefs:
https://github.com/faithandbrave/Shand/blob/master/shand/duration.hpp
...
template <class Rep>
using seconds_t = std::chrono::duration<Rep>;
using seconds_f = seconds_t<float>;
using seconds_d = seconds_t<double>;
using seconds_ld = seconds_t<long double>;
template <class Rep>
using minutes_t = std::chrono::duration<Rep, std::ratio<60>>;
using minutes_f = minutes_t<float>;
using minutes_d = minutes_t<double>;
using minutes_ld = minutes_t<long double>;
...
These durations usage is here:
#include <iostream>
#include <shand/duration.hpp>
int main()
{
std::chrono::seconds int_s(3);
shand::minutes_f float_m = int_s; // without `duration_cast`
std::cout << float_m.count() << std::endl; // 0.05
}

C++ Integer overflow problem when casting from double to unsigned int

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)));
}
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
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.
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.