Why does gcc accept "int a = 3i;" as a valid statement? - c

When I write the following C code, I find that (contrary to what I expected), gcc can accept this code and compile it successfully! I don't know why for it seems it is a wrong statement.
int main() {
int a = 1i;
return 0;
}
I guess it may be accepting 1i as a complex number. Then int a = 1i means int a = 0+1i and 1i is not a valid integer so it only accepts the 0.
int main() {
int a = 1i*1i;
printf("%d\n",a);
return 0;
}
I tried the code above and found that it printa -1. Maybe my thought is correct. But this is the first time I find that the C compiler can do this work. Is my guess correct?

Your intuition is correct. gcc allows for complex number constants as an extension.
From the gcc documentation:
To write a constant with a complex data type, use the suffix i or
j (either one; they are equivalent). For example, 2.5fi has type
_Complex float and 3i has type _Complex int. Such a constant always has a pure imaginary value, but you can form any complex value you
like by adding one to a real constant. This is a GNU extension; if you
have an ISO C99 conforming C library (such as GNU libc), and want to
construct complex constants of floating type, you should include
<complex.h> and use the macros I or _Complex_I instead.
So 1i is the complex number i. Then when you assign it to a, the complex part is truncated and the real part is assigned (and if the real part is a floating point type, that would be converted to int).
This conversion is spelled out in section 6.3.1.7p2 of the C standard:
When a value of complex type is converted to a real type, the
imaginary part of the complex value is discarded and the value of the
real part is converted according to the conversion rules for the
corresponding real type.

Related

What is the order in which C expressions are evaluated

int main(){
char a = 5;
float b = 6.0;
int c = a + b;
return c;
}
Looking at the generate instructions with gcc, the above code is evaluated like this:
Load 5 and convert it to float as a
Load 6 as b
Add a and b
Convert the result to an integer and return
Does gcc not care about the return type while it's dealing with the expression?
It could have converted b to an int right off the bat as everything else is an integer type.
Is there a rule which explains how one side of an expression is evaluated first regardless of what the other side is?
You ask "Is there a rule?" Of course there is a rule. Any widely used programming language will have a huge set of rules.
You have an expression "a + b". a has type char, b has type float. There's a rule in the C language that the compiler has to find a common type and convert both to the common type. Since one of the values is a floating-point type, the common type must be a floating-point type, which is float, double, or long double. If you look closer at the rules, it turns out the common type must be float or double, and the compiler must document this. It seems the compiler chose "float" as the common type.
So a is converted from char to float, b is already float, both are added, the result has type float. And then there's a rule that to assign float to int, a conversion takes place according to very specific rules.
Any C compiler must follow these rules exactly. There is one exception: If the compiler can produce the results that it is supposed to produce then it doesn't matter how. As long as you can't distinguish it from the outside. So the compiler can change the whole code to "return 11;".
In the C language, partial expressions are evaluated without regard how they are used later. Whether a+b is assigned to an int, a char, a double, it is always evaluated in the same way. There are other languages with different rules, where the fact that a+b is assigned to an int could change how it is evaluated. C is not one of those languages.
If you change it to:
int main(){
char a = 5;
float b = 6.6;
int c = a + 2*b;
return c;
}
then it becomes clear that you have to keep the float 18.2 until the end.
With no optimizations, gcc acts as if this could happen and does a lot of conversions.
With just -O it already does the math itself and directly returns the final integer, even in above example.
There is no in-between reasoning and short-cut here. Why simplify from 5+6.0 to 5+6 but not to 11? Either act stupid and do cvtsi2ss xmm1,eax (and back etc.), or then tell them directly 11.

long double in fabs, range and overflow errors

At wiki.sei.cmu.edu, they claim the following code is error-free for out-of-range floating-point errors during assignment; I've narrowed it down to the long double case:
Compliant Solution (Narrowing Conversion)
This compliant solution checks whether the values to be stored can be represented in the new type:
#include <float.h>
#include <math.h>
void func(double d_a, long double big_d) {
double d_b;
// ...
if (big_d != 0.0 &&
(isnan(big_d) ||
isgreater(fabs(big_d), DBL_MAX) ||
isless(fabs(big_d), DBL_MIN))) {
/* Handle error */
} else {
d_b = (double)big_d;
}
}
Unless I'm missing something, the declaration of fabs according to the C99 and C11 standards is double fabs(double x), which means it takes a double, so this code isn't compliant, and instead long double fabsl(long double x) should be used.
Further, I believe isgreater and isless should be declared as taking a long double as their first parameters (since that's what fabsl returns).
#include <stdio.h>
#include <math.h>
int main(void)
{
long double ld = 1.12345e506L;
printf("%lg\n", fabs(ld)); // UB: ld is outside the range of double (~ 1e308)
printf("%Lg\n", fabsl(ld)); // OK
return 0;
}
On my machine, this produces the following output:
inf
1.12345e+506
along with a warning (GCC):
warning: conversion from 'long double' to 'double' may change value [-Wfloat-conversion]
printf("%lg\n", fabs(ld));
^~
Am I therefore correct in saying their code results in undefined behavior?
On p. 211 of the C99 standard there's a footnote that reads:
Particularly on systems with wide expression evaluation, a <math.h> function might pass arguments
and return values in wider format than the synopsis prototype indicates.
and on some systems long double has the exact same value range, representation, etc. as double, but this doesn't mean the code above is portable.
Now I have a related question here, and I'd just like to ask for confirmation (I've read through dozens of questions and answers here, but I'm still a little confused because they often deal with specific examples and specific types, not all of them are sourced, or they're about C++, and I think it'd be a waste of time to ask each of these questions as a separate, "formal" question on Stack Overflow): according to the C99 and C11 standards, there's a difference between overflow, which occurs during an arithmetic operation, and a range error, which occurs when a value is too large to be represented in a given type. I've provided excerpts from the C99 standard that talk about this, and I'd appreciate it if someone could confirm that my interpretation is correct. (I'm aware of the fact that certain implementations define what happens when undefined behavior occurs, e.g. as explained here, but that's not what I'm interested in right now.)
for floating-point types, overflow results in some representation of a "large value" (i.e. as defined by the HUGE_VAL* macro definition as per 7.12.1):
A floating result overflows if the magnitude of the mathematical result is finite but so
large that the mathematical result cannot be represented without extraordinary roundoff
error in an object of the specified type. If a floating result overflows and default rounding
is in effect, or if the mathematical result is an exact infinity (for example log(0.0)),
then the function returns the value of the macro HUGE_VAL, HUGE_VALF, or HUGE_VALL according to the return type, with the same sign as the correct value of the
function;
On my system, HUGE_VAL* is defined as INFINITY cast to the appropriate floating-point type.
So this is completely legal, the value of HUGE_VAL* being implementation-defined or something like that notwithstanding.
for floating-point types, a range error results in undefined behavior (6.3.1.5):
When a double is demoted to float, a long double is demoted to double or
float, or a value being represented in greater precision and range than required by its
semantic type (see 6.3.1.8) is explicitly converted to its semantic type [...]. If the value being converted is outside the range of values that can be represented, the behavior is undefined.

casting in c can cause any compile error?

I know that in java I can't return double from a function that supose to return an int value without casting, but now when I learn c I see that I can compile something like (only warnings, no error):
int calc(double d, char c) {
return d * c / 3;
}
so my question is, c compiler will always do for me auto casting when needed?
or this is specific working one because of char or something?
C has a concept of implicit conversions, i.e. rules that define under which conditions and how values are converted to different types implicitly, without the need to explicitly cast them (see, for example, cppreference.com). So C is not "auto-casting" everything, but only under certain conditions.
Your return type is int, whereas the result of expression d * c / 3 is double. So the following (implicit) conversion applies:
Real floating-integer conversions
A finite value of any real floating
type can be implicitly converted to any integer type. Except where
covered by boolean conversion above, the rules are: The fractional
part is discarded (truncated towards zero). If the resulting value can
be represented by the target type, that value is used otherwise, the
behavior is undefined
Actually, I think that you may not get any warning. The compiler is performing an implicit conversion, and the only way to force it is adding the flag -Wconversion (I am talking about gcc, something like: warning: conversion to ‘int’ from ‘double’ may alter its value [-Wfloat-conversion]). Anyway, the compiler supposes that you know about it, I mean, the decimal value dissapears in case of an integer conversion.
(See YePhIcK's comment about MSVC)

Specify float when initializing double. gcc and clang differs

I tried running this simple code on ideone.com
#include<stdio.h>
int main()
{
double a = 0.7f; // Notice: f for float
double b = 0.7;
if (a == b)
printf("Identical\n");
else
printf("Differ\n");
return 0;
}
With gcc-5.1 the output is Identical
With clang 3.7 the output is Differ
So it seems gcc ignores the f in 0.7f and treats it as a double while clang treats it as a float.
Is this a bug in one of the compilers or is this implementation dependent per standard?
Note: This is not about floating point numbers being inaccurate. The point is that gcc and clang treats this code differently.
The C standard allows floating point operations use higher precision than what is implied by the code during compilation and execution. I'm not sure if this is the exact clause in the standard but the closest I can find is §6.5 in C11:
A floating expression may be contracted, that is, evaluated as though it were a single operation, thereby omitting rounding errors implied by the source code and the expression evaluation method
Not sure if this is it, or there's a better part of the standard that specifies this. There was a huge debate about this a decade ago or two (the problem used to be much worse on i386 because of the internally 40/80 bit floating point numbers in the 8087).
The compiler is required to convert the literal into an internal representation which is at least as accurate as the literal. So gcc is permitted to store floating point literals internally as doubles. Then when it stores the literal value in 'a' it will be able to store the double. And clang is permitted to store floats as floats and doubles as doubles.
So it's implementation specific, rather than a bug.
Addendum: For what it is worth, something similar can happen with ints as well
int64_t val1 = 5000000000;
int64_t val2 = 5000000000LL;
if (val1 != val2) { printf("Different\n"); } else { printf("Same\n"); }
can print either Different or Same depending on how your compiler treats integer literals (though this is more particularly an issue with 32 bit compilers)

Type safety for complex arithmetic in C99

I'm using C's complex arithmetic support and ran into some surprising and undesirable behavior. Complex values seem to be implicitly convertible to real ones, discarding the imaginary part.
Consider the following:
#include <complex.h>
#include <stdio.h>
float complex getComplex(float real, float imag)
{
return real + imag * I;
}
int main()
{
const float /* missing complex! */ c = getComplex(25, 6) / getComplex(2, 4);
printf("%f + %f J\n", crealf(c), cimag(c));
return 0;
}
I would expect to receive some warning that I am assigning a float complex to a float or perhaps calling crealf and cimagf on a non-complex value, but this builds cleanly on GCC 5.1, even with -Wall -Wextra -Wconversion switches. Clang 3.6.1 at least emits such a warning:
wut.c:11:62: warning: implicit conversion discards imaginary component: '_Complex float' to 'float'
[-Wconversion]
const float /* missing complex! */ c = getComplex(25, 6) / getComplex(2, 4);
~ ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
Why can complex values "collapse" to real ones? Is there some verbiage about this in the standard? And is there some additional switch for GCC that can provide warnings about this behavior? It has already bitten us, and switching toolchains (to clang) for this project isn't feasible at present time.
C11 final draft (N1570), 6.3.1.7 paragraph 2:
When a value of complex type is converted to a real type, the imaginary part of the complex value is discarded and the value of the real part is converted according to the conversion rules for the corresponding real type.
Real types are integer and real floating types.
The C99 rationale explains the motivation behind this:
In mathematics, when a complex is converted to real, the imaginary part is discarded. C99 follows the same convention.
And (numbering is identical to C11):
The specification in §6.3.1.6 and §6.3.1.7 match Fortran's.

Resources