chiva
chiva

Reputation: 11

Why is the output always zero when the fractions are parenthesized? No matter what the input is

Problem statement: A cookie recipe calls for the following ingredients:

The recipe produces 48 cookies with these amounts of the ingredients. Write a program that ask the user how many cookies he or she wants to make and then displays the number of cups of each ingredient needed for the specified number of cookies.

#include<iostream>
using namespace std;

int main(void){
    double sugar, butter, flour;
    int cookies;

    cout << "Enter the number of cookies you wish to make: ";
    cin >> cookies;

    sugar = (double)cookies*(1/32);
    butter = (double)cookies*(1/48);
    flour = (double)cookies*(11/192);

    cout << "Sugar: " << sugar << endl;
    cout << "butter: " << butter << endl;
    cout << "flour: " << flour << endl;

    return 0;
}

Upvotes: 1

Views: 172

Answers (2)

Paul Floyd
Paul Floyd

Reputation: 6906

To understand expression evaluation like this, you need to know three things

  1. The types of literals and variables.
  2. The order of evaluation of the sub-expressions.
  3. The conversion rules that apply to expressions.

There's a lot more to integer literals than meets the eye, though for 'ordinary' numbers you can consider that all integer literals are of type int. Take a look at cpprefernce for more details.

Roughly speaking, sub-expression evaluation follows the BODMAS or PEMDAS rules. Obviously C++ has far more operators, which complicate matters. The principle is the same though. Again cppreference has a thorough summary.

Lastly, there is the question of sub-expressions conversions. At the end, the result of the whole expression needs to be that of the type of the left hand side of the assignment operator. Before that, the sub-expressions may also undergo conversions. To simplify this, a sub-expression containing two different integral types will convert the smaller to the larger size. Sub-expressions containing integral and floating point values will convert to floating point. Once again cppreference has all the details, this time not so easy to follow.

So what does this mean for your expressions?

Let's take

sugar = (double)cookies*(1/32);

cookies, 1 and 32 are all of type int. However, cookies is explicitly cast to double. Note here that the cast operator has higher precedence than multiplication, so cookies is converted to double and not the result of the rest of the expression.

So in terms of types we have

double coookies*(int 1/int 32)

As already noted, the result of the integer division is zero, so we have

double cookies*int 0

The zero is implicitly convreted to zero double

double cookes*double 0.0

And the final result is zero double

(Note that the order of evaluation could be different as it is not specified, I'll spare you a fourth cppreference link).
(Note 2 for code like this the compiler will optimize sub-expressions. Even with no optimization I see the subexpression 1/12 gets evaluated as 0 on Compiler Explorer, and with high optimization the compiler can do all of the evaluation. This does not change the concepts here).

So what should you do? I would suggest doing as @bruno says and simply using floating point literals.

Upvotes: 0

bruno
bruno

Reputation: 32586

sugar = (double)cookies*(1/32);
butter = (double)cookies*(1/48);
flour = (double)cookies*(11/192);

all of them value 0 because you divide int

use float for at least one number in each division

you can also just divide by the right number for the two first cases, to multiply by 1 is useless

sugar = cookies/32.0;
butter = cookies/48.0
flour = cookies*(11.0/192);

But because the statement says 1.5 cups of sugar for 48 cookies it seems more coherent to use sugar = cookies*1.5/48.0; it is useless to simplify the numbers, the compiler does that at compile time and produces exactly the same code in the two cases.

Same for 2.75 cups of flour for 48 cookies, flour = cookies*2.75/48;

In case you want to use these formulas in several locations in your code it is more practical and safe to define constants like

const double sugar_per_cookie = 1.5/48;
const double butter_per_cookie = 1.0/48;
const double flour_per_cookie = 2.75/48;

again do not be afraid of cost at execution, the compiler will optimize. If you prefer you can also use #define for them but in that case warning to place the formulas between () to be compatible with any formula using them and to not produce a bad result because of operator precedence.

I also encourage you to check if >> success, if you do not enter a valid integer cookies is not set

if (!(cin >> cookies)) {
  cerr << "invalid value, abort" << endl;
  return -1;
}

So for instance:

#include <iostream>

int main(void){
  std::cout << "Enter the number of cookies you wish to make: ";

  int cookies;

  if (!(std::cin >> cookies) or (cookies < 0)) {
    std::cerr << "invalid number of cookies, abort" << std::endl;
    return -1;
  }

  double sugar = cookies*1.5/48.0;
  double butter = cookies/48.0;
  double flour = cookies*2.75/48;

  std::cout << "Sugar: " << sugar << std::endl;
  std::cout << "Butter: " << butter << std::endl;
  std::cout << "Flour: " << flour << std::endl;

  return 0;
}

Compilation and execution:

pi@raspberrypi:~ $ g++ -Wall c.cc
pi@raspberrypi:~ $ ./a.out
Enter the number of cookies you wish to make: 48
Sugar: 1.5
butter: 1
flour: 2.75
pi@raspberrypi:~ $ ./a.out
Enter the number of cookies you wish to make: 0
Sugar: 0
butter: 0
flour: 0
pi@raspberrypi:~ $ ./a.out
Enter the number of cookies you wish to make: 1
Sugar: 0.03125
butter: 0.0208333
flour: 0.0572917
pi@raspberrypi:~ $ ./a.out
Enter the number of cookies you wish to make: 10
Sugar: 0.3125
butter: 0.208333
flour: 0.572917
pi@raspberrypi:~ $ ./a.out
Enter the number of cookies you wish to make: -1
invalid number of cookies, abort
pi@raspberrypi:~ $ ./a.out
Enter the number of cookies you wish to make: aze
invalid number of cookies, abort
pi@raspberrypi:~ $ 

Upvotes: 4

Related Questions