Why does a float when converted to an int be rounded off below in C?
Asked Answered
G

6

-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?

Gautier answered 26/1, 2016 at 7:14 Comment(7)
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?Treadway
@user2864740, oops, I did not know that. Sorry, then can you explain me why this happens.Gautier
@juanchopanza, Yes I am interested in why the language says it that way.Gautier
I suggest reading at least one good book on C before trying to get a job as a C programmer.Rammer
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?Maltose
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.Mazman
There is no rounding happening here. Just truncation. The fractional part is ignored and the integer part is assigned to the int.Showiness
S
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.

Shaquitashara answered 26/1, 2016 at 7:49 Comment(0)
S
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.

Scimitar answered 26/1, 2016 at 7:44 Comment(4)
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.Shaquitashara
@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.Scimitar
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."Shaquitashara
@Lundin: this kind of self-righteousness is so counter-productive! Nobody is immune from typos.Scimitar
P
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.

Pulpy answered 26/1, 2016 at 7:19 Comment(4)
No need for the after commentary. It's easy to look back (forgetfully) after putting in 10 thousand hours.Ivo
What about this: float f = 10000000000000; char a = f;Saxena
@Treadway 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. blog.frama-c.com/index.php?post/2013/10/09/…Maltose
@PascalCuoq Indeed it is! Nice blog BTW.Treadway
R
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.

Retractor answered 26/1, 2016 at 18:15 Comment(0)
A
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.

Aflutter answered 24/8, 2023 at 12:26 Comment(1)
Does this add anything new, which hasn't been stated in other answers?Palawan
G
-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;
      ^
Glance answered 26/1, 2016 at 7:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.