We learned in Intro to Programming that if you divide two integers, you always get an integer. To fix the problem, make at least one of those integers a float.
Why doesn't the compiler understand I want the result to be a decimal number?
Software Engineering Stack Exchange is a question and answer site for professionals, academics, and students working within the systems development life cycle. It only takes a minute to sign up.
Sign up to join this communityWhy doesn't the compiler understand I want the result to be a decimal number?
The C++ compiler is simply following well-defined and deterministic rules as set forth in the C++ standard. The C++ standard has these rules because the standards committee decided to make it that way.
They could have written the standard to say that integer math results in floating-point numbers, or does so only in the case of a remainder. However, that adds complexity: I either need to know ahead of time what the result is, or maybe convert back to integer if it always gives a float. Maybe I want an integer.
One of C++'s core philosophies is "you do not pay for what you do not use." If you actually want the complexity of mixing integers and floats (and the extra CPU instructions and memory accesses this entails1), then do the type cast as you mentioned in your question. Otherwise, stick to standard integer math.
Finally, mixing integral and floating point variables can result in loss of precision and sometimes incorrect results as I discuss below. If you want this, then pay for it: otherwise, the standard dictates that compilers stick to a strict set of rules for mixing data types. This is well-defined behavior: as a C++ developer, I can look this up in the standard and see how it works.
There are essentially three ways to do what you are trying to do, each with benefits and drawbacks.
Integer math: this results in truncating results during division as you found out. If you want the decimal portion, you need to treat that separately by dividing, getting the remainder, and treating the decimal portion as the remainder divided by the divisor. This is a bit more complex of an operation and has more variables to juggle.
Floating-point math: this will generally produce correct (enough) results for small values, but can easily introduce errors with precision and rounding, especially as the exponent increases. If you divide a large number by a small number, you may even cause an underflow or simply get a wrong result because the scales of the numbers do not play nicely with each other.
Do your own math. There are classes out there that handle extended precision of decimal and rational numbers. These will typically be slower than math on built-in types, but are generally still pretty quick and provide arbitrary-precision math. Rounding and other issues are not automatic like they are with IEEE floats, but you gain more control and certainly more accuracy.
The key here is to choose based on the problem domain. All three methods of representing numbers have their own benefits and drawbacks. Using a loop counter? Pick an integral type. Representing locations in 3D space? Probably a group of floats. Want to track money? Use a fixed-decimal type.
1 Most popular CPU architectures (e.g. x86-64) will have separate sets of instructions that operate on different register types such as integer and floating point, plus extra instructions to convert between integral, floating point, and various representations of them (signed and unsigned, float and double). Some of these operations may entail memory access as well: convert a value and store it to memory (its variable). Math at the CPU level is not as simple as "integer in, float out." While adding two integers may be a very simple operation, possibly a single instruction, mixing data types can increase the complexity.
This is due to the evolution of hardware. Back in the early days of computers, not all machines had a floating point unit, the hardware was simply not able to understand the notion of a floating point number. Of course, floating point numbers can be implemented as a software abstraction, but that has significant downsides. All of the arithmetic on these machines had to be pure integer arithmetic by default.
And still today, there is a firm distinction between integer and floating point arithmetic units within a CPU. Their operands are stored in separate register files to start with, and an integer unit is wired to take two integer arguments and produce an integer result that ends up in an integer register. Some CPUs even require an integer value to be stored to memory, and then reloaded back into a floating point register, before it can be recoded into a floating point number, before you can perform a floating point division on it.
As such, the decision made by the C developers back in the very beginning of the language (C++ simply inherited this behavior), was the only appropriate decision to make, and remains of value today: If you need floating point math, you can use it. If you don't need it, well you don't have to.
10 / 3
.
10 / 2 with integers gives you exactly 5 - the correct answer.
With floating point math, 10 / 2 might give the correct answer*.
In other words, it is impossible for floating point numbers to be "perfect" on current hardware - only integer math can be correct, unfortunately it can't do decimal places but there are easy work arounds.
For example instead of 4 / 3, do (4 * 1000) / (3 * 1000) == 1333. Just draw a . in software when displaying the answer to your user (1.333). This gives you an accurate answer, instead of one that's incorrect by some number of decimal places.
Floating point math errors can add up to cause significant errors - anything important (like finance) will use integer math.
*the 10 / 2 example will actually be correct with floating point math, but you can't rely on it, many other numbers give incorrect results... for more details do some reading: http://http.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps The point is, you can't rely on accuracy whenever floating points are involved
Although technically not completely correct, C++ is still considered a superset of C, was inspired by it and as such appropriated some of its properties, integer division being one of them.
C was mostly designed to be efficient and fast, and integers are generally much faster than floating points, because the integer type is tied to hardware, whereas floating points need to be calculated.
When the /
operand recieves two integers, one of the left side and one on the right, it may not even do division at all, the result may be computed using simple addition and a loop, asking how many times does the operand on the right side fit into the operand on the left.
//
is the integer-division operator and#
is a single-line comment. In Pascal, the integer-division operator is the keyworddiv
. Both work quite well for their respective languages. C does probably the worst thing possible: one operator that can do two completely different things based on arbitrary context.