1

I'm quite new to C, and recently I have discovered some (to me) "weird" behavior in some of its arithmetic operators.

Since 'f' is a float, I expect the result of '3 / 2' to be 1.5 (as in the second example), but somehow it is the result of an integer division.

In the second example, since 3.0 and 2.0 are both floats, the result now is (as expected) 1.5.

And finally, in the third example, 3.0 is a float, 2 is an integer. The result of '3.0 / 2' is 1.5, thus the result of a float division.

I therefore conclude that there are two different types of division: A integer division of form [int] / [int] = [int] and a float division [float] / [float] = [float]. In the third example, before dividing, '2' is simply casted to a float. In the first example the integer division '3 / 2' is performed, and afterwards casted to a float (thus the value 1.0).

Is this the actual way how C "interprets" arithmetic operations? Am I right with my assumptions?

f = 3 / 2;          // f = (float) (3 / 2)
printf("%f\n", f);  // 1.000000

f = 3.0 / 2.0;      //
printf("%f\n", f);  // 1.500000

f = 3.0 / 2;        // f = 3.0 / (float) 2
printf("%f\n", f);  // 1.500000
Win32
  • 149
  • 1
  • 9
  • 5
    While seeking knowledge is a good thing, you are supposed to do some research before posting a question. A search for "c integer division" yields thousands of relevant links. Please read [ask] And yes, your assumptions are correct. – klutt Nov 01 '19 at 22:40
  • Read https://en.cppreference.com/w/c/language/conversion, specifically Usual arithmetic conversions. It applies to division. – Brady Dean Nov 01 '19 at 22:47
  • `3.0` and `2.0` are `double`, not `float`. – Eric Postpischil Nov 01 '19 at 23:05
  • You may be overthinking things. In C, the default numeric type is `int`, so absent something designating a number as something other than `int` (like a cast or suffix), the number will be treated as an `int`. – David C. Rankin Nov 02 '19 at 01:44

4 Answers4

1

Since 'f' is a float, I expect the result of '3 / 2' to be 1.5 (as in the second example), but somehow it is the result of an integer division.

Yes, the result of the operation is an integer because the two operands (the literal values) are integers. The reference for C Arithmetic Operators explains:

The binary operator / divides the first operand by the second (after usual arithmetic conversions) following the usual arithmetics definitions, except that when the type after usual arithmetic conversions is an integer type, the result is the algebraic quotient (not a fraction), rounded in implementation-defined direction (until C99) truncated towards zero (since C99)

So you will get integer arithmetic unless one of the operands is a float, in which case type promotions come into effect. You can use a suffix to make a literal into a float, or use a cast to make a variable into a float.

I therefore conclude that there are two different types of division: A integer division of form [int] / [int] = [int] and a float division [float] / [float] = [float].

Yes, that is essentially correct: the types of the operands to the operators determine the resulting type of the expression. (It is not determined by the type of assignment.)

gavinb
  • 19,278
  • 3
  • 45
  • 60
1

In C, there are constants and literals. These are numeric constants (not to be confused with const variables), and they can have suffixes that specify the type of number it is. An f as in 2.1f informs the compiler that this is a float constant, meanwhile, plain 2.1 is a double constant.

The type of the operands determine the type of the result of an arithmetic expression. So, even if mathematically 3 / 2 == 1.5, to a C compiler it means 3 / 2 == 1 since the operands are integers, and a truncation is performed.

Your conclusion is thereby correct.

Javier Silva Ortíz
  • 2,864
  • 1
  • 12
  • 21
1

Is this the actual way how C "interprets" arithmetic operations?

It's more complex.

First, types narrower than int/unsigned are promoted int (or unsigned if the value may exceed INT_MAX) . Then prior to arithmetic operator evaluation, the arguments are promoted to a common type. The lower ranking type is promoted to the higher ranking type: int, unsigned, long, unsigned long, long long, unsigned long long, float, double, long double** and onto complex is needed.

Note that the type of the left of the = has nothing to do with the math operation on the right.

Constants like 2.0 are type double and 2.0/3.0 form a double quotient. Since 2.0/3.0 is not exactly representable, the result is rounded to a nearby double - before assignment. If the result is stored in a float, another rounding may occur.


I therefore conclude that there are two different types of division:

Consider more than 2 different possible quotients who arguments only differ in type.

int main() {
  printf("%.17f\n", (double) (2/3));
  printf("%.17f\n", (double) (2/3.0f));
  printf("%.17f\n", (double) (2/3.0));
  // long double may have yet another quotient.
}

Output

0.00000000000000000
0.66666668653488159
0.66666666666666663

Note: Floating point may evaluate to a higher rank than either operand depending on FLT_EVAL_METHOD.


** This is a slight over simplification.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

try this

f = 3 / 2;          printf("%f\n",f); //1.000000
f = (float)(3 / 2); printf("%f\n",f); //1.000000
f = (float)3 / 2;   printf("%f\n",f); //1.500000
f = 3 / (float)2;   printf("%f\n",f); //1.500000
f = 3 / 2.0;        printf("%f\n",f); //1.500000
f = 3.0 / 2;        printf("%f\n",f); //1.500000
f = 3.0 / 2.0;      printf("%f\n",f); //1.500000

Parse this like a compiler would. Yes f is declared as a float, but the number 3 without a decimal point is an integer. When there is a decimal point then C dictates that is a double. In order to perform an operation the operands are promoted to the highest/largest/most precise-ish, whatever, float is above int, double is above float.

f = 3 / 2;          printf("%f\n",f); //1.000000

3 and 2 are integers the division happens first as an integer division result of 1 then the equals is done second the 1 needs to be promoted to a float.

f = (float)(3 / 2); printf("%f\n",f); //1.000000

same as above the division comes first it is isolated within the parenthesis so integer division, then it is promoted to float, the equals is next and the result is already promoted to float.

f = (float)3 / 2;   printf("%f\n",f); //1.500000

3 is promoted to float first so in order to perform the division 2 has to be promoted to float, the division is done as a float and then the storage to f it is already float.

f = 3 / (float)2;   printf("%f\n",f); //1.500000

same as above 2 is promoted from an int to float then the division has to be float so 3 is promoted.

f = 3 / 2.0;        printf("%f\n",f); //1.500000

2.0 is a double as written like this in C so 3 is promoted to a double, the division is double. that result has to be converted from double to float then stored to f as a float.

f = 3.0 / 2;        printf("%f\n",f); //1.500000

same as above 3.0 is a double, 2 is promoted, division is double, then converted to float.

f = 3.0 / 2.0;      printf("%f\n",f); //1.500000

both are double.

Now I just added this one:

f = 3.0F / 2;      printf("%f\n",f); //1.500000

adding the F makes 3 a single float, 2 is promoted to float, division is float then the answer is stored in f.

Unless you do no optimization, most of these "conversions" are optimized out where possible, if you compiled the above or your program with optimizations on the compiler is going to simply generate a list of printfs of 1.0f or 1.5F. The comments describe how to read/interpret the code, how the compiler is going to read that code and functionally implement it.

old_timer
  • 69,149
  • 8
  • 89
  • 168