26
int a{5},b{2},c{9};
double d = (double)a / (double)b + (double)c;

Or I can use static_cast. Either way is verbose, especially when the formula is long. Is there a better solution?

Milo Lu
  • 3,176
  • 3
  • 35
  • 46
  • Have you tested this? Does it work? – Thomas Matthews Jun 12 '16 at 17:26
  • Pick a style and be consistent. Move on. When in doubt, check the assembly language listing for all your different cases. Choose the one that is most readable. – Thomas Matthews Jun 12 '16 at 17:27
  • 2
    The cast to double emphasizes the conversion from int, which is good for anybody reading the code.. –  Jun 12 '16 at 17:31
  • Why aren't you using `double` variables to begin with? – milleniumbug Jun 12 '16 at 17:32
  • 1
    @milleniumbug That would make the question pointless. –  Jun 12 '16 at 17:34
  • @Dieter Lücking I can agree with you on that. However, if one `(double)` missing in a long formula, the result will be wrong. Such bugs are difficult to detect. – Milo Lu Jun 12 '16 at 17:37
  • 2
    @milleniumbug is onto something here. Unless this is a throwaway example, you should just declare the variables as type double. If they need to be of type int, then you should use an explicit static_cast. That makes the code self-documenting. Yes, it is ugly, but so is a cast. They're supposed to be ugly so they call people's attention to them. – Cody Gray - on strike Jun 12 '16 at 17:44
  • 1
    `static_cast` may be verbose, but at least it allows you to easily search for casts in your code and conveys to anyone reading it that there are runtime penalties (however small they may be) being taken at that line. – RyanP Jun 12 '16 at 17:52
  • 1
    @RyanP: A `static_cast` does not necessitate a runtime penalty. In fact, they are usually free. Assuming an x86 architecture, for this specific piece of code a compiler would generate code to load an integer to the TOS, instead of a floating point value (`fild` vs. `fld`). No penalty at all. – IInspectable Jun 12 '16 at 17:56
  • 1
    @IInspectable good to know, thanks! Would it also avoid any penalties if you were doing something like `static_cast(i) / static_cast(i+j)`? – RyanP Jun 12 '16 at 18:30
  • 1
    @RyanP: You'd have to inspect the generated object code on a case-by-case basis. There are other factors that play into this (e.g. values need to be stored from registers into memory to load them back into the FPU, for example). But more often than not a `static_cast` will not produce any code. Besides, the performance penalty is not really, what a reader should be informed about. It's the explicit conversion. – IInspectable Jun 12 '16 at 18:36
  • Even I went through this thought process and explored. This is the most reliable way which works in all situations. – crazydan Apr 22 '20 at 13:00
  • `int a{5},b{2},c{9}; double d = static_cast(a / (b + c));` Even I went through this thought process and explored. This is the most reliable way which works in all situations. – crazydan Apr 22 '20 at 14:06

3 Answers3

19

You can multiply by 1.0:

int a{5}, b{2}, c{9};
double d = 1.0 * a / b + 1.0 * c;

And when you work with sums you can add to 0.0:

double d = 0.0 + a - b + c;

Most compilers perform optimization such that the meaningless operation is not evaluated. Only type conversion is done.


Remember that you only need to cast the first member in each division/multiply group. Do so in any manner that seems reasonable. And simple addition/substraction (with no other type multipliers/divisors) is casted too. Compilers guarantee casting. So your example:

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

Really may be rewritten like this:

double d = (double)a / b + c;
double d = 1.0 * a / b + c;
double d = static_cast<double>(a) / b + c;

Some more examples:

double d = (double)a / b + (double)c / d + e;
double d = 1.0 * a / b + 1.0 * c / d + e;
double d = static_cast<double>(a) / b + static_cast<double>(c) / d + e;
Kyle
  • 1,070
  • 2
  • 13
  • 23
oklas
  • 7,935
  • 2
  • 26
  • 42
  • 3
    If something is questionable ("most compilers ") you should not do it. –  Jun 12 '16 at 17:33
  • When somebody concerned such thin details he need to 1. read target compiler manual or 2. build performance tests on it or 3. use debugger to view generated code to check carefully. I think possible old compilers like K&R do not do this but current compiler developers want to be actual. – oklas Jun 12 '16 at 17:43
  • 14
    Rewriting, what is meant to be a cast to a meaningless multiplication is a really poor suggestion. If I were to read the code, I'd have to stop and think, inspect the code history, probably remove the useless multiplication, re-compile, only to find out, that this should have been a `static_cast` from the beginning. Don't do this. Use a cast, when you need to convert between types. – IInspectable Jun 12 '16 at 17:49
  • 1
    Hard coding number without to change it to named constands is bad pattern at all. So when we see 1.0 multipliers at such content naturally suppose to cast. You are right, when you teach students say this of course. But qualified developers see such touch on fly easily. There books of its. For example "Hacker's Delight" and so on. I am does not meet command or people who have problem to understand this. – oklas Jun 12 '16 at 18:01
  • those operations are not meaningless in IEEE754 – Sopel Jul 25 '19 at 19:54
4

Is there a better solution?

Yes. Express intent through functions.

Marvel as the optimiser emits perfectly efficient assembler. Enjoy the accolades of your colleagues who gaze in wonder at your awesomely readable and maintainable code:

#include <iostream>

auto a_over_b_plus_c(double a, double b, double c)
{
  double d = a / b + c;
  return d;
}

int main()
{
  int a = 5, b = 2, c = 9;

  std::cout << a_over_b_plus_c(a, b, c) << std::endl;
}

For fun, here's a solution based on tuples & lambdas:

#include <iostream>
#include <tuple>

template<class T, class...Args> 
auto to(Args&&...args)
{
  return std::make_tuple(T(std::forward<Args>(args))...);
}

int main()
{
  int a = 5, b = 2, c = 9;

  auto calc = [](auto&& vals) {
    auto& a = std::get<0>(vals);
    auto& b = std::get<1>(vals);
    auto& c = std::get<2>(vals);

    return a / b + c;
  };

  auto result = calc(to<double>(a, b, c));

  std::cout << result << std::endl;
}

... and something perhaps more readable...

#include <iostream>
#include <tuple>
#include <complex>

template<class T, class F, class...Args> 
auto with(F f, Args&&...args)
{
  return f(T(std::forward<Args>(args))...);
}



int main()
{
  int a = 5, b = 2, c = 9;

  auto calc = [](auto&& a, auto&& b, auto&& c) {

    return a / b + c;
  };

  auto result = with<double>(calc, a, b, c);
  auto result2 = with<float>(calc, a, b, c);
  auto result3 = with<std::complex<double>>(calc, a, b, c);
  auto result4 = with<std::complex<float>>(calc, a, b, c);

  std::cout << result << std::endl;
  std::cout << result2 << std::endl;
  std::cout << result3 << std::endl;
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • 4
    @Titulum: `auto` is not a type. I cannot stress this enough. – Lightness Races in Orbit Jun 12 '16 at 19:36
  • @LightnessRacesinOrbit I wrote it out of habit. I wasn't looking to make a statement. If you think it's dumb and misleading, please by all means edit the answer. – Richard Hodges Jun 12 '16 at 19:50
  • 1
    Bad habit! You should try to kick that :) – Lightness Races in Orbit Jun 12 '16 at 19:56
  • @LightnessRacesinOrbit can you give me the reason you think it's a bad habit? – Richard Hodges Jun 12 '16 at 20:08
  • @RichardHodges: Probably not in the space allotted for comments (by the system) or the time allotted for comments (by me) :) I'll just leave you with the phrase "early failure detection". – Lightness Races in Orbit Jun 12 '16 at 20:09
  • @LightnessRacesinOrbit really? nothing? not even a link to someone's "best practice" page? And there was me thinking that auto return types were useful when writing generic code, and committed no harm when writing simple functions, accessor methods and lambdas - where the return type is explicitly auto by very carefully considered language design. I don't know - maybe it's me, but I always found that spelling out overly long template-expanded return types was a little... tedious. And what if I wanted to return a lambda? Their types are unutterable - again by design. I say you're blowing smoke. – Richard Hodges Jun 12 '16 at 20:18
  • 2
    @RichardHodges: That's your right. If you really want to know, read through the old Lounge transcripts. I don't have the time to dig up the research for you today though. NB. this is _not_ generic code, and `double` is not an "overly long template-expanded return type". Or a lambda. So I say you're in the straw industry. – Lightness Races in Orbit Jun 12 '16 at 20:50
  • @LightnessRacesinOrbit You made the extraordinary claim that it's a "bad" habit. I think it's up to you to provide the evidence. But I don't think you will. I think you're just trolling. Enjoy. – Richard Hodges Jun 12 '16 at 20:55
  • 1
    *"auto return types were useful when writing generic code"* - It is, and that's where it should be used. `a_over_b_plus_c`, however, is not at all generic code, and using `auto` here doesn't add anything. It is, in a way, deliberately destructive even, as you now no longer see the return type, or the author's documented intended return type. I believe this is a case, where even the *"Almost Always auto"*-Sutter wouldn't suggest using the keyword. At least [Joseph Mansfield would disagree](http://josephmansfield.uk/articles/dont-use-auto-unless-you-mean-it.html). – IInspectable Jun 13 '16 at 11:19
  • Besides, your decision to **not** use `auto` for `int a = 5, b = 2, c = 9;` seems a bit arbitrary. – IInspectable Jun 13 '16 at 11:24
  • 1
    @IInspectable guilty as charged. consistency in coding style is not one of my strong points. – Richard Hodges Jun 13 '16 at 11:28
4

This works but all you need is a single 1.0* in front of a

int a{5},b{2},c{9};
double d = (double)a / (double)b + (double)c;

int a{5},b{2},c{9};
double d = 1.0*a / b + c;

The rules of precedence and implicit conversion will cause all the variables to be converted to doubles.

One thing to be careful of is grouped variables which will need to have their own 1.0* or 0.0+ as appropriate:

int a{5},b{2},c{9};
double d = a / (0.0 + b + c);

int a{5},b{2},c{9};
double d = a / (1.0 * b * c);

Alternately, one use use a static cast on the associated variable. I prefer the smaller version as the 1.0* or 0.0+ both scream out implicit conversion to doubles.

int a{5},b{2},c{9};
double d = a / (static_cast<double>(b) * c);
doug
  • 3,840
  • 1
  • 14
  • 18