6

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?

12
  • 8
    Maybe you want the result to be an integer instead. How can it tell the difference? – soandos Jan 21 '16 at 20:21
  • 12
    Because the C++ standard says so. – user22815 Jan 21 '16 at 20:26
  • 4
    @soandos: The way Pascal and Python both do it: have a distinct integer-division operator, but the standard division operator always returns the mathematically correct result. (Or--for the pedantic--as correct as you can get given the limitations of FP math.) – Mason Wheeler Jan 21 '16 at 20:27
  • 3
    How would the compiler know you want the result to be a decimal number? There are valid reasons to want an integer. – Bryan Oakley Jan 21 '16 at 20:27
  • 5
    @soandos In Python, // is the integer-division operator and # is a single-line comment. In Pascal, the integer-division operator is the keyword div. 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. – Mason Wheeler Jan 21 '16 at 20:38
15

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

13
  • 2
    You say the C++ standard stipulates that that behavior should be so. Why? Wouldn't it make things easier to say, "Division of integers that are not evenly divisible results in floats, any other division is fair game." – moonman239 Jan 21 '16 at 20:59
  • @moonman239 see my edits. – user22815 Jan 21 '16 at 21:12
  • 4
    @moonman239 Not for compiler writers. Many commonly used CPU architectures provide an integer result when asked to do division with two integers. They would have to implement a check for non-integer results and then switch to use the slower floating point instructions. Alternatively, they could have defaulted to floating point division and lost the interest of those who wanted fast math, those who wanted accurate math, and those who were used to C. Changing now isn't an option because that would break compatibility with existing code. – 8bittree Jan 21 '16 at 21:13
  • 1
    Not that you would be advocating this as an alternative but making the static type of an expression depend on the run-time values of the operands won't work with C++'s static type system. – 5gon12eder Mar 6 '16 at 16:56
  • 3
    @moonman239: Having an operation that produces a different type depending on the values of the operands is sheer madness. – gnasher729 Mar 7 '16 at 11:19
8

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.

3
  • Its sad that most of the constraints that existed at the creation of the C++ standard is pretty obsolete today! For example: "you do not pay for what you do not use." nowadays, hardware is taken for granted and all users wants is execution! – mahen23 Jan 22 '16 at 8:30
  • 4
    @mahen23 Not all users think like this. I work in a field where programs are run on thousands of CPU-cores in parallel. In this area, efficiency is money, both in terms of investments and in terms of sheer power consumption. A language like Java does not stand the ghost of a chance in that area, while C++ does. – cmaster - reinstate monica Jan 22 '16 at 17:00
  • @mahen23 No it isn't - or better, it only is if you look at current CPU architectures for desktops and above. There are still many embedded systems that don't or only partially support floating point operations, and C as well as C++ continue to support them in order to provide the most efficient implementation possible short of using assembler. BTW, even higher level languages like Python distinguish between integer and FP operations - try 10 / 3. – Murphy Mar 6 '16 at 9:26
1

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

2
  • 1
    IEEE 754 compliant floating-point implementations will give you an exact result for 10 / 2. In fact, they will give you exact results for any operation involving only integer operands that have an integer result provided that operands and result can be represented exactly, which “small enough” integers can. – 5gon12eder Mar 6 '16 at 16:52
  • @5gon12eder there's no need to nit pick, I'm just trying to describe a complex problem in simple terms. The entire point of supporting non-integer values is to have decimal places (which can be done using integers by simply multiplying everything by the number of decimal places you want as I demsonstraed). – Abhi Beckert Mar 6 '16 at 20:41
0

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.