G++ -Wall Not Warning About Double-> Int Cast

Strange GCC short int conversion warning

When you do arithmetic computations, the operands are subject to "the usual arithmetic conversions" (a superset of the "integer promotions" quoted in Acme's answer—he beat me to this but I'll go ahead and post anyway :-) ). These widen short int to plain int, so:

a + b

computes the same result as:

((int) a) + ((int) b)

The return statement must then narrow this int to a short int, and this is where gcc produces the warning.

warning: use of old-style cast in g++

reinterpret_cast, static_cast, dynamic_cast and const_cast are the c++ cast alternatives.

  • const_cast to remove const/volatile from a const variable.
  • dynamic_cast to perform runtime validity checks when casting in between polymorphic types
  • static_cast to perform e.g up/down-cast in a inheritance hierarchy, but with no runtime checks, or to explicitly perform conversions that could be implicit (e.g. float to int)
  • reinterpret_cast to convert in between unrelated types.

Brief syntax example:

char* a = (char*) b; 
//would be
char* a = static_cast<char*>(b);
//to remove the warning

Explicit casting in C/C++

A. The initialization

double sign = -2 * (num % 2) + 1;

is perfectly well-defined. That's what I'd use; I don't think there's any need to complicate things with extra casts or anything.

C and C++ are well-defined and convenient in their implicit conversions between integer and floating-point types. Explicit conversions are usually not needed. In my experience there are only three things to worry about:

  • Code like double ratio = 1 / 3 doesn't do what you want; you need to force one of the operands to / to be floating-point. (This has nothing to do with your question, but it's an extremely easy mistake to make.)
  • Overflow, if one type or the other can't represent the value. (Also not a problem for your example.)
  • Overzealous compilers. Many compilers will "helpfully" warn you that you might lose precision when converting from double to float, or from a floating-point type to an integer. So you may need explicit casts to silence those warnings.

B. Asking for the numeric value of a Boolean is perfectly well-defined (is guaranteed to give you a nice, clean, 1 or 0), so your second fragment should be fine also. (I know this is true for C, and per a comment below, it's true for C++ also.)

Is the behaviour of casting a negative double to unsigned int defined in the C standard? Different behaviour on ARM vs. x86

No


This conversion is undefined and therefore not portable.

C99/C11 6.3.1.4

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.

According to C11 6.3.1.4 footnote 61:

The remaindering operation performed when a value of integer type is converted to unsigned type need not be performed when a value of real floating type is converted to unsigned type. Thus, the range of portable real floating values is (−1, Utype_MAX+1).

Warnings on sign conversion when assigning from the result of unsigned division

Clang is just being an extra bit clever: division of an unsigned integer by an unsigned integer larger or equal to 2 means the unsigned integer result will always fit in the signed integer counterpart (to that of the numerator type in the division expression). However, if you divide by an unsigned integer valued 1, the result is no longer guaranteed to fit in a signed integer counterpart, and Clang does emit a warning:

#include <cstdint>

int main() {
uint8_t a = 240; // '240 / 1' will not fit in int8_t
int8_t e = a / 2u; // No warning in clang
int8_t f = a / 1u; // warning: implicit conversion changes signedness: 'unsigned int' to 'int8_t' (aka 'signed char') [-Wsign-conversion]
}

One could also argue that Clang should be able to omit the warning for the similar special case of multiplication by 0; however Clang does not whereas GCC, instead, does:

// Clang warns, no warning in GCC.
int8_t g = a * 0u;

So peculiarly Clang is clever, in this context, w.r.t. division and GCC w.r.t. multiplication.


Finally, note that the gating for Clang to emit this warning during division seems to be only when dividing by 1, as you will not get the same -Wsign-conversion if you divide by 0u; arguably as it has been overridden by the more relevant (in such a context) -Wdivision-by-zero warning:

int8_t h = a / 0u;
warning: division by zero is undefined [-Wdivision-by-zero]

How to fix C++ warning of implicit conversion?

This warning is triggered by the -Wsign-conversion switch, detecting that you're taking a signed variable and converting it to an unsigned variable, in a way that may change the value.

It doesn't do it for positive literals, where the conversion obviously doesn't change the value, because that would be pointless and really annoying. You'll get it for a negative literal like -5.

(Technically, this is the literal 5 with the unary negation operator applied, not a "negative literal"!).

For named variables it can't really predict what the value would be, so errs on the side of caution.

You should make your variable start have type size_t.

gcc scanf warning believes float is double

Which leads me to the question, why do gcc believe the second argument
of scanf to be a double, when it actually is a float?

Because float is promoted to double as specified in C Standard

6.5.2.2 Function calls

[#6] ... arguments
that have type float are promoted to double. These are
called the default argument promotions.

[#7] ... The ellipsis notation in a function prototype
declarator causes argument type conversion to stop after the
last declared parameter. The default argument promotions
are performed on trailing arguments.

C++ : Implicit type conversion

See my comments.

This is well-defined. The intermediate expression for z will undergo widening into double, so y * z will be a double expression. An implicit narrowing conversion will then convert it to float for storing in res2. This same narrowing applies to res1.

This is reflected by §5¶9 Expressions [expr] of the C++11 standard.

Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows:

...

  • Otherwise, if either operand is double, the other shall be converted to double.
  • Otherwise, if either operand is float, the other shall be converted to float.

...

This, however, does not sure that the equality will hold.

That being said, res1 need not necessarily be equivalent to res2 -- it is highly dependent on the precision of float and double in the environment. The two literals could potentially not even be equal -- 4.23423451f not need even be equivalent to 4.23423451. You can not be sure that static_cast<double>(static_cast<float>(4.23423451)) will be equal to 4.23423451.

See §5.17¶3 Assignment and compound assignment operators [expr.ass].

If the left operand is not of class type, the expression is implicitly converted (Clause 4) to the cv-unqualified type of the left operand.

§4 Standard conversions [conv] states as follows:

Standard conversions are implicit conversions with built-in meaning. Clause 4 enumerates the full set of such conversions. A standard conversion sequence is a sequence of standard conversions in the following order:

...

  • Zero or one conversion from the following set: integral promotions, floating point promotion, integral conversions, floating point conversions, floating-integral conversions, pointer conversions, pointer to member conversions, and boolean conversions.

As elaborated in §4.6 Floating point promotion [conv.fpprom],

  1. A prvalue of type float can be converted to a prvalue of type double. The value is unchanged.
  2. This conversion is called floating point promotion.

... and §4.8 Floating point conversions [conv.double],

  1. A prvalue of floating point type can be converted to a prvalue of another floating point type. If the source value can be exactly represented in the destination type, the result of the conversion is that exact representation. If the source value is between two adjacent destination values, the result of the conversion is an implementation-defined choice of either of those values. Otherwise, the behavior is undefined.

  2. The conversions allowed as floating point promotions are excluded from the set of floating point conversions.

The problem here is that we have multiple cases where our conversion is not promotion, but rather narrowing to a potentially lower-precision type (double to float).

Essentially, any time you convert double to float, you may potentially lose precision.



Related Topics



Leave a reply



Submit