Search code examples
c++cfloating-pointinteger-promotion

Deterministic way of saying "promote everything to floating before calculation" in C++


Given that I'd prefer to keep numbers in my program as ints or whatever integral, what is the most convenient way of doing an arbitrary arithmetic with floating point equivalents of those numbers?

Say, I have

int a,b,c,d;
double x;

And I want to write

x=a/b/c/d+c/d+a;

without turning the expression into mess by putting conversions everywhere in parsed operator tree leafs like

x=(double)a/b/c/d+(double)c/d+a;

Is it doable with C-style macro (recursive or not)? Should it be done with new class and overloaded operators?


Solution

  • x=a/b/c/d+c/d+a;
    

    This is a pretty complex expression. Better give it a name:

    double complex_expression(double a, double b, double c, double d) {
      return a/b/c/d+c/d+a;
    }
    

    Now when you call that with integer arguments, since the parameters are of type double the arguments get converted to double using the usual arithmetic conversions:

    int a,b,c,d;
    // Init them somehow
    double x = complex_expression(a,b,c,d);
    

    With a C++11 lambda ...

    int a,b,c,d;
    // Init them somehow
    double x = [](double a, double b, double c, double d) {
      return a/b/c/d+c/d+a; }(a,b,c,d);
    

    ... works, but IMO somehow looks clumsy.

    Slightly better?

    double x = [a = (double)a, b = (double)b, c = (double)c, d = (double) d] {
        return a/b/c/d+c/d+a; }();
    

    Oh, and if you're in for some macro fun:

    #define SPLICE_2(l,r) l##r
    #define SPLICE_1(l,r) SPLICE_2(l,r)
    #define SPLICE(l,r) SPLICE_1(l,r)
    
    #define TREAT_AS(type, name) name = static_cast<type>(name)
    
    #define TREAT_ALL_AS_HELPER_0(type)
    #define TREAT_ALL_AS_HELPER_1(type, name)  TREAT_AS(type, name)
    #define TREAT_ALL_AS_HELPER_2(type, name, ...) TREAT_AS(type, name), TREAT_ALL_AS_HELPER_1(type, __VA_ARGS__)
    #define TREAT_ALL_AS_HELPER_3(type, name, ...) TREAT_AS(type, name), TREAT_ALL_AS_HELPER_2(type, __VA_ARGS__)
    #define TREAT_ALL_AS_HELPER_4(type, name, ...) TREAT_AS(type, name), TREAT_ALL_AS_HELPER_3(type, __VA_ARGS__)
    #define TREAT_ALL_AS_HELPER_5(type, name, ...) TREAT_AS(type, name), TREAT_ALL_AS_HELPER_4(type, __VA_ARGS__)
    // expand as you will
    
    #define TREAT_ALL_AS(type, count, ...) SPLICE(TREAT_ALL_AS_HELPER_, count)(type, __VA_ARGS__)
    

    Now use as

    double x = [TREAT_ALL_AS(double, 4, a, b, c, d)] {
      return a/b/c/d+c/d+a; }();
    

    You can also automatically count the number of variadic macro arguments.


    But to be honest, IMO its best to just write a named function.