I'm getting in to C++ using the book by Stephen Prata: C++ Primer Plus. As part of explaining functions, listing 7.4 illustrates an odds calculator, which calculates a binomial coeffcient. The code is inserted below (slightly modified to typecast the long double to long for screen printing).
The problem I have is the statement: odds = odds * ntotal / i;
When I use this form, the function returns the correct answer, e.g. binomial(36,7) = 8,347,680. However, if I use the compound assignment:
odds *= ntotal / i,
the function returns binomial(36,7) = 6,462,720, which is wrong.
If these expressions are equivalent, how come they yield different answers?
Thanks for enlightening me.
Br,
redak
------------------------------------------------
// lotto.cpp
#include <iostream>
long double probability(unsigned, unsigned);
int main()
{
using namespace std;
double total, choices;
cout << "Enter the total number of choices on the game card and\n"
"the number of picks allowed:\n";
while ((cin >> total >> choices) && choices <= total)
{
cout << "You have one chance in ";
cout << (long) probability(total, choices); // compute the odds
cout << " of winning.\n";
cout << "Next two number (q to quit): ";
}
cout << "Bye!\n";
return 0;
}
long double probability(unsigned ntotal, unsigned ndraw)
{
long double odds = 1.0;
for (int i = 1; i <= ndraw; i++, ntotal--)
{
odds = odds * ntotal / i;
}
for (double i = 1; i <= ndraw; i++, ntotal--)
{
odds *=( ntotal / i);
}
I try to cast the i to double , then it works well,and prints 8347680. It suggests that when you calculate (ntotal/i),if i is an integer, the precision would lost.
I try to cast the i to double , then it works well,and prints 8347680. It suggests that when you calculate (ntotal/i),if i is an integer, the precision would lost.
This appears to be the right answer. Apparently, the compiler promotos the unsigned ntotal and i in the expression odds = odds * ntotal / i, while it is not clever enough to promote them in the compound assignment, so it performs integer division and the fractions are lost.
it's not about compiler being "clever", it's about compiler obeying the language requirements. If the arguments to some operator are mismathed, they are promoted so that they match, and the return type of the operator also matches what the arguments were promoted to. The compiler has no freedom in this case.
you are wrong. It depends only on the decision adopted by the C/C++ Standard Committee. In fact, as in operator op= the left operand also takes part as an operand for the operator op it could be adopted that the left operand also is promoted. And in this case compilers were more clever and give the same result for two operators
It depends only on the decision adopted by the C/C++ Standard Committee
Correct, and a compiler that does the opposite has a bug (in case of such trivial expression evaluation, a major bug). As for a op= b vs. a = a op b, the compiler does in fact treat them the same (as it should). That's not where the difference was: see @Zhuge's answer.
What does mean "as it should"? As I pointed out it is one of the possibilities and has its drawback which makes two statements
a op= b; and a = a op b; non-equivalent though it is more naturally to do them equivalent.
The phrase about more clever compilers is ironical and can be interpretated that the C/C++ committee could be more clever.
@vlad What do you call "one of the possibilities"? Are you suggesting that a "clever" compiler should not follow operator precedence rules whenever they don't feel "natural" (to who?)
I suggest that a op= b; will be equivalent to a = a op b;
The order of the operator precedence can be preserved, but the left ooperand shall take part in the operation twice.
For example consider for based on range. You write
for ( const auto &x : v ) { /* some code */ }
but you know that it changed to compound construction that uses begin( v ) and end( v ). So the code of a op= b could be converted by compilers to a = a op b;
I already said about the precedence. There is no any problem with the precedence if accoording to the updated Standard compilers shall comvert internally the operation a op= b; to a = a op b;
I already said about the precedence. There is no any problem with the precedence if accoording to the updated Standard compilers shall comvert internally the operation a op= b; to a = a op b;
There is promotion.
First it calculates the integer division, then promotes that integer to a double for the multiplication.
Because of the parenthesis, the order is fixed, and that's what should happen.