-1

In an interview, I was asked what do I think about the following code:

#include <stdio.h>

int main()
{
    float f = 10.7;
    int a;
    a = f;
    printf ("%d\n", a);
}

I answered:

  • The compiler will emit a warning as you are changing a float to an int without a cast.

  • The int will have garbage value as you are not using a cast.

Then, they allowed me to run the program on a online compiler. I was overwhelmed. Both my assumptions were wrong. The compiler did not emit any warnings, and the int had the value 10. Even when I changed, the float to a value like 10.9, or 10.3, the answer was the same. Even putting a cast did not change the result.

Can someone tell me why this happens and in what cases will the result be different.

NOTE: While compiling, the interviewer told me to add no gcc flags.


EDIT: Now I have understood, that the float does not get rounded off, and the answer will be 10. But can someone explain me why is this designed like this? Why does any float when converted to an int, be rounded below? Is there a specific reason?

Box Box Box Box
  • 5,094
  • 10
  • 49
  • 67
  • 5
    For warnings it depends on the compiler and its settings. For the behaviour, it is the way it is specified. Are you interested in *why* the language spec says it is that way? – juanchopanza Jan 26 '16 at 07:16
  • 1
    @user2864740, oops, I did not know that. Sorry, then can you explain me why this happens. – Box Box Box Box Jan 26 '16 at 07:17
  • @juanchopanza, Yes I am interested in why the language says it that way. – Box Box Box Box Jan 26 '16 at 07:18
  • I suggest reading at least [one good book on C](http://stackoverflow.com/questions/562303/the-definitive-c-book-guide-and-list) before trying to get a job as a C programmer. – Paul R Jan 26 '16 at 07:20
  • 2
    I would not expect a warning, since what must happen on an assignment of a `float` expression to an `int` variable is completely specified by the C standard. How should the compiler know that the programmer does not intend exactly that to happen? – Pascal Cuoq Jan 26 '16 at 07:31
  • as far as i know, it's faster to round this way. If you want to round correctly, you can just add +0.5 before rounding. – Flikk Jan 26 '16 at 07:37
  • 2
    There is no rounding happening here. Just truncation. The fractional part is ignored and the integer part is assigned to the `int`. – Spikatrix Jan 26 '16 at 08:52

6 Answers6

9

This is what the standard 6.3.1.4 says:

When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is undefined.

The undefined behavior part is regarding size and signedness of the integer. If you picked an integer which is too small to contain the result, it invokes undefined behavior.

The int will have garbage value as you are not using a cast

The int may have a garbage value in case of overflow, but the presence or lack of cast has nothing to do with it.

A compiler is never required to show a "warning", warnings are not something specified by the C standard, which only speaks of diagnostics (that is, some kind of message, call it error or warning). Therefore you can never portably assume that every compiler will give a warning of any kind.

In this case of implicit float-to-int conversion, the compiler is not required to display any form of diagnostic at all. Good compilers will, however.

In the case of GCC, it is rather sloppy about such warnings, you have to tell it explicitly to give the warnings by adding -Wconversion or -Wfloat-conversion. These are the extra flags hinted at.

Lundin
  • 195,001
  • 40
  • 254
  • 396
2

Many conversions are implicit in C:

  • between all numeric types,
  • between data pointer types and the void * type,
  • from an array type to its corresponding pointer type (this is called decaying)
  • from a non const type to its const equivalent,

Compilers usually do not issue diagnostics about these because the behavior is defined by the standard, but some of these conversions indicate a programming error, such as double x = 1 / 2;. To help programmers avoid these silly mistakes, the compiler can be instructed to issue warnings or even errors.

It is wise to use these extra warnings. gcc will do that if invoked with -Wall -Wextra -Wconversion -Wfloat-conversion and clang has similar settings: clang -Wall -Weverything. Your first assumption was not unrealistic, but gcc is notoriously lenient with sloppy code by default and you were instructed to not use any flags. Even worse: to stay compatible with old Makefiles, gcc defaults to c89 and will complain about the use of some c99 features.

Note that the language defines the behavior when it can determine that they are necessary, but sometimes it cannot:

float f = 10.7;
printf("%d\n", f);

In this case, the standard specifies that f should be passed to the variadic function printf as a double, but printf expects an int argument for the specifier %d. Your second assumption would be correct here, an explicit conversion with an (int) cast is required. Again, good compilers can issue a diagnostic for these errors if instructed to do so.

Furthermore, some implicit conversions can be determined at compile time to lose information of even invoke undefined behavior, such as:

char x = 300;
int x = 1e99;

It would help if the compiler issued diagnostics for these even without a strict option.

Lastly, some conversions will loose information but are more difficult to detect in the general case:

double f = 10000000000000;
char a = f;
float f = d;
int i = d;

The language does define the behavior only if the receiving type is large enough for the integral part, otherwise the behavior is undefined even with an explicit cast. Whether the programmer wants a warning or not in these cases is a matter of personal choice.

Regarding why the conversion from a floating point type to an integer type is defined to round toward 0, it might be simpler to do so, and you can get other behaviors by using round() or adding 0.5 before the conversion if you know the value is positive.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • I don't think `-Wall -Wextra` is enough to get the warning from GCC. It seems you have to explicitly add `-Wconversion` or `-Wfloat-conversion` on top of those. Now imagine if -Wall actually enabled all warnings... how useful it would be. – Lundin Jan 26 '16 at 07:52
  • @Lundin: This might be. I don't know if you are being sarcastic... I do think it would be useful, there would be at least half as many C questions on Stackoverflow! I personally use `-Wall -Wextra -Werror` with only very few `-Wno-xxx` adjustments. – chqrlie Jan 26 '16 at 07:56
  • I was being sarcastic at the typical "GNU logic": `-Wall This enables all the warnings about constructions that some users consider questionable, and that are easy to avoid`. In English: "This does not enable all warnings, but rather enables an arbitrary collection of warnings which we have subjectively picked with no rationale given." – Lundin Jan 26 '16 at 08:01
  • @Lundin: this kind of self-righteousness is so counter-productive! Nobody is immune from typos. – chqrlie Jan 26 '16 at 08:06
0

There will be a warning depending on compiler settings.

But according to the C language Standard, the behaviour is absolutely well defined, and the floating point value will be rounded towards zero. a is guaranteed to be 10. Since your answer falsely indicates that about every C program in the world is fatally broken, I'd not hire you for a C job.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
  • 2
    No need for the after commentary. It's easy to look back (forgetfully) after putting in 10 thousand hours. – user2864740 Jan 26 '16 at 07:22
  • 1
    What about this: `float f = 10000000000000; char a = f;` – artm Jan 26 '16 at 07:22
  • @juanchopanza Sorry, overflow in the conversion from floating-point to integer is undefined behavior also for unsigned types, and is undefined behavior in artm's example unless `char` can represent 10000000000000. http://blog.frama-c.com/index.php?post/2013/10/09/Overflow-float-integer – Pascal Cuoq Jan 26 '16 at 15:27
  • @PascalCuoq Indeed it is! Nice blog BTW. – juanchopanza Jan 26 '16 at 15:44
0

C has a conversion defined between almost everything, so, hardly any assignments produce warnings or errors unless you enable extra checks not required by the specification.

As to why it rounds toward zero, well, if it added (or subtracted) 0.5 prior to truncation in order to round, then you would have to undo this with open code if what you wanted was truncation, so you aren't any worse off than if you have to add 0.5 in order to round.

Also, C is a practical language and it was important that its normal behavior correspond with the available instructions on actual machines of its original era.

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
0

When a float is implicitly converted to an int by the compiler, it's not rounded below or even rounded. The fractional part of the number is 'truncated' i.e. anything after the decimal point is simply removed.

Shreyaa
  • 1
  • 2
-3

I think your reasoning makes perfect sense. C, however, typically doesn't make perfect sense; floating point values are implicitly rounded towards zero. You can make the compiler issue a warning when types are implicitly converted like this.

/*test.c*/

#include <stdio.h>

int main()
{
    double f;
    int a;

    f = 10.7;
    a = f;
    printf("%d\n", a);
    return 0;
}

With GCC the option we are looking for is -Wconversion:

$ c89 -pedantic -Wall -Wconversion test.c
test.c: In function ‘main’:
test.c:9:6: warning: conversion to ‘int’ from ‘double’ may alter its value [-Wfloat-conversion]
  a = f;
      ^
August Karlstrom
  • 10,773
  • 7
  • 38
  • 60