10

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?

Euri Pinhollow
  • 322
  • 2
  • 16
  • 2
    You should use `x=(double)a/b/c/d+(double)c/d+a;`, your version has 2 integer divisions (`a/b/c`). – mch Oct 06 '16 at 08:12
  • 1
    You can reference [this](http://en.cppreference.com/w/cpp/language/implicit_conversion) article, "Numeric promotions" part - becuase some automatical type promotion (from integral to floating point) happens, you can skip some explicit type casts. – Steeve Oct 06 '16 at 08:14
  • @mch: `/` has left-to-right associativity meaning that `a/b/c/d` will be grouped as `a/(b/(c/d))`, and if I convert `a` then `b/c/d` will be integer division. Correct me if I am wrong. – Euri Pinhollow Oct 06 '16 at 08:17
  • 2
    You are wrong, `/` having left to right associativity means that `a/b/c/d` will be calculated as `((a/b)/c)/d`. From left to right, you explained right to left. – mch Oct 06 '16 at 08:27

1 Answers1

11
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.

Community
  • 1
  • 1
Daniel Jour
  • 15,219
  • 2
  • 27
  • 57
  • 2
    @KishanKumar Then it's even better to have individual expressions *named* in order to reduce overall complexity. Functions aren't expensive, you can have lots of them (though you might want to make them static then, assuming they're only used once) – Daniel Jour Oct 06 '16 at 08:24
  • 1
    Could you add a version with C++11 anonymous functions? – Euri Pinhollow Oct 06 '16 at 08:33
  • IMHO your "Slightly better" version is slightly worse. – Chris Drew Oct 06 '16 at 08:47
  • 1
    @ChrisDrew I've added a question mark ;) I think both versions are a nightmare regarding readability ... – Daniel Jour Oct 06 '16 at 09:04
  • 1
    @DanielJour Personally, I would put the lamda in a named `auto` variable. You might say it offers no advantages over a normal named function but it reduces its scope and places it close to where it is being used. – Chris Drew Oct 06 '16 at 10:00
  • Is there any place to learn how to do advanced macro programming like that? – 0x499602D2 Oct 06 '16 at 10:13
  • @kishan-kumar: I am using C++ for years and I still cannot think out a way of doing the questioned tasked effectively. The most logical answer is "do not store floating operands as integrals" or "do not use C++ for that". Really though, there should not be undocumented complex arithmetic operations, there is always a limited number of them. – Euri Pinhollow Oct 06 '16 at 10:29
  • Nice, nifty solutions :) – Viktor Sehr Oct 06 '16 at 11:15
  • FWIW, all this to avoid having to write a `(double)` cast twice? Really? – Rudy Velthuis Oct 06 '16 at 13:01
  • @RudyVelthuis yeah i think its a lot of effort to just not right the float. And making a function for every expression which will be mostly used once, is also not good. As functions are made for those codes which are to be reusable. – Kishan Kumar Oct 06 '16 at 13:36
  • @DanielJour if he had many calls to different function then some part of the runtime will be wasted in these function calls. better make them inline. – Kishan Kumar Oct 06 '16 at 13:42
  • @0x499602d2: use IEC standard as reference. It might not be educative but it will list every possibility available. – Euri Pinhollow Oct 06 '16 at 13:51
  • 1
    @ChrisDrew That sounds reasonable, at least if the function's only used in a narrow part of the code. – Daniel Jour Oct 06 '16 at 21:42
  • 1
    @RudyVelthuis Yes, as long as it improves readability. The macro solution though is probably only relevant when there's a lot of such complex expressions... – Daniel Jour Oct 06 '16 at 21:44
  • 1
    @KishanKumar No, that's not how `inline` works, at least not since the last 10 years or so. The compiler will inline a function whenever it deems this appropriate. Marking such utility functions as `static` should be done though, otherwise the function names are exported as symbols in the resulting object file/binary. – Daniel Jour Oct 06 '16 at 21:47
  • 1
    @KishanKumar "As functions are made for those codes which are to be reusable" I'd say this is a common fallacy. Functions are an abstraction that allows us to *name* some (collection of) operations and then later refer to that using that name (possibly multiple times). – Daniel Jour Oct 06 '16 at 21:48
  • @0x499602D2 Not that I know of. Basically a combination of trial and error, looking at other peoples code and reading the standard. – Daniel Jour Oct 06 '16 at 21:51
  • @DanielJour Okay. And why do you do `#define SPLICE(l,r) SPLICE_1(l,r)` instead of `#define SPLICE(l,r) l##r`? – 0x499602D2 Oct 06 '16 at 23:40
  • @0x499602D2 Oops, that's actually unnecessary: You need this to "force evaluation" of constructs like `SPLICE(thing, macro(foo))` (i.e. to get the concatenation of `thing` and whatever `macro(foo)` expands into, instead of `thingmacro(foo)`). I use that `SPLICE` because I can be relatively sure that it does what I mean (splice the results after macro expansion). – Daniel Jour Oct 08 '16 at 22:04