0

Is there a way to write the operation short and suppress the warning?

BYTE data[10];
int i = 0;    
int offset = 0;

for (i = 0; i < 7; i++) {
    // some code
    data[offset] |= (1 << i);  // WARNING: conversion may loose information
}

my attempts:

data[offset] |= (BYTE) (1 << i); // failed
data[offset] |= (1 << (BYTE) i); // failed

Compiler: TIA-V13 WinCC from Siemens

Solution: No compound assignment operators!

Wolf
  • 9,679
  • 7
  • 62
  • 108
raiserle
  • 677
  • 8
  • 31
  • 4
    There is no `short`. And show the definition of `BYTE`. – too honest for this site Nov 27 '15 at 10:49
  • You may want to add the compiler info that produces the warning and how `BYTE` is defined. – P.P Nov 27 '15 at 10:51
  • 2
    This code is correct and your compiler is giving a false warning. Perhaps investigate how to turn off that warning for this section of code, or consult your compiler documentation for this warning. To get answers about a specific compiler you will have to mention which compiler and version you are using – M.M Nov 27 '15 at 10:51
  • 1 - Please show definition of `BYTE` (I assume it is `short`). 2 - Let us know the size in bytes of the type `BYTE` and/or `short` on your machine. 3 - Show us a copy of the output please. – Ely Nov 27 '15 at 10:57
  • You can use bts assembly instruction: http://stackoverflow.com/questions/1983303/using-bts-assembly-instruction-with-gcc-compiler – sibnick Nov 27 '15 at 11:13
  • @M.M: The warning is nasty, but actually correct. e.g. gcc generates such warnings. – too honest for this site Nov 27 '15 at 11:22
  • What compiler are you using, and what flags? `gcc -pedantic -Wall` does not issue anything, with `BYTE` either as `unsigned char` or `unsigned short`. I *do* get your error when I typedef `BYTE` as something totally illogical, such as `float`. – Jongware Nov 27 '15 at 11:28
  • 1
    @Jongware: For gcc it is `-Wconversion`. Bad enough; the switch is otherwise very useful. – too honest for this site Nov 27 '15 at 11:35
  • @sibnick: I'd rather use `pragmas` around before before reverting to assembler just to suppress a warning. This is not about optimising. – too honest for this site Nov 27 '15 at 12:00
  • WHy is the compiler warning even an issue to begin with. If you know your code is correct, and doing exactly what you want, why do you care about compiler warnings on it? – Magisch Nov 27 '15 at 12:27
  • 2
    @Magisch: Many development standards make warnings errors and require compilation without any warning. The option which generates these warnings are very useful in general, so one should enable it. And there are very well use-cases of a compound-assignments which should be warned about. – too honest for this site Nov 27 '15 at 12:36
  • @Olaf the warning is not correct. The warning says "may lose information" however there is no possibility for this code to lose information. – M.M Nov 27 '15 at 21:33
  • @M.M: It very well does: `1 << (sizeof(int) * CHAR_BIT - 1)` will (presuming the it is the MSB) loose the sign of the result. – too honest for this site Nov 28 '15 at 07:06
  • @Olaf your expression does not appear in OP's code. `1` is shifted by a maximum of `6` bits. – M.M Nov 28 '15 at 11:40
  • @M.M: You cannot expect the compiler to perform a full dynamic code analysis to suppress the warning. As give, the compiler _has to assume_ `i` is an arbitrary, yet valid, value. – too honest for this site Nov 28 '15 at 11:43
  • @Olaf I am saying that the warning message is false for this exact code. I'm not making any statement about what a compiler should or should not do. – M.M Nov 28 '15 at 11:45
  • @M.M: False? Nope. But unwarranted. As are many warnings which are safely silenced by explicit casts, as it is possibly for this warning, too. Do you really expect a compiler to analyse the code for every warning if it is suitable? – too honest for this site Nov 28 '15 at 12:02
  • @Olaf it is false. The warning says "may lose information" but in fact no information can be lost by this exact code. Please re-read my last comment: I do not make any statement about what the compiler should do. I am only commenting on whether or not this code may lose information. Please explain how you think information is lost in OP's exact code. – M.M Nov 28 '15 at 12:04
  • @M.M: Ok, last try: The warning says "...**may** loose information", which include the no-lose case. Thus the warning is true. Anything else has already been written. – too honest for this site Nov 28 '15 at 12:26

1 Answers1

4

You did not define BYTE; I'll assume it is identical to stdint.hs uint8_t (which should always be preferred to homebrew types).

a |= b (roughly) translates to a = a | b, including integer promotions here as part of the usual arithmetic conversions. For the types given this means a is converted to int before the operation is performed, yielding an int result. The final assignment will truncate this int for the assignment, possibly loosing information (likely the sign), because int is at least 16 bits wide.

Casting b does not help, because the conversion is done for the operation and you have to cast the result.

The transformation is also the solution:

uint8_t a;
a = (uint8_t)(a | b);

Explicitly tells the compiler you know what you do and to shut up. Just make sure you really know what you do!

This is not very elegant, but the only way to suppress the warning. OTOH the behaviour is arithmetically consistent with the simple assignment-version.

Edit: You should use unsigned integer constants when shifting where possible. Even more, as you operate on unsigned variables anyway. As a general rule, try to avoid mixing signed and unsigned operands. If that is not possible make sure you know what is going on and catch all invalid cases.

Edit2: For some implementations/signed integer representations and some combinations for types and operations, it is possible to prove no information can be lost, thus the warning is obsolete. However, it seems this would require knowledege of machine details which might not be available at the stage the warning is generated.

Maybe the C standard should have defined the compound assignment to implicitly include the final cast. That would not be the only legacy. OTOH, this behaviour might have generated confusion in the other direction to people which were not aware of this (note that in C an assignment yields a result!). Which is the lesser of two evils?

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
  • I can agree with your thorough explanation. Except that if all operands of bitwise OR are bytes then any integer promotion before the operation can never result in loss of information when the compiler truncates the result back to a byte. And the compiler should know this and (indeed) shut up. – Paul Ogilvie Nov 27 '15 at 12:12
  • @PaulOgilvie: Well, it **can**. That because the operation is performed `int`, thus signed. Anyway, the standard does not go that far actually. It just converts the operands to `int` (so the `|` **is** performed on `int`, not `uint8_t`) and the result is `int`, too. Note that things become even more complicated if the result was e.g. `int8_t`. Then the result was implementation-specific. So, while I too find this behaviour somewhat nasty, it is correct. – too honest for this site Nov 27 '15 at 12:23
  • No, it _can't_ ;oose information. Sign-extending a byte and applying bitwise OR and then truncating back to byte will still not loose any information. Yes, the upper sign-extended one bits are lost, but they were never a part of the byte anyway. Again, in my opinion the compiler should know and shut up. – Paul Ogilvie Nov 27 '15 at 12:50
  • @PaulOgilvie: You just think about 2s complement representation. But the standard does not require this, but also e.g. sign/magnitude, which would loose the sign very well. – too honest for this site Nov 27 '15 at 12:53
  • The compiler is TIA-V13 WinCC from Siemens. I've not defined BYTE and i can't find the definition. And i cannot tell the compiler to ignore this warning. The only way is: the long arithmetic operation. ``data[offset] = (BYTE)(data[offset] | (1 << i));`` – raiserle Nov 27 '15 at 13:15
  • I don't know what compiler that is, but I have the same issue with gcc. And yes, you need a _simple assignment_, not a [_compound asignment_](http://port70.net/~nsz/c/c11/n1570.html#6.5.16.2). Is that what you called "short arithmetic operation? - if so, please use the correct name; `short` and `long` are C integer datatypes which interferes with the subject of your question. – too honest for this site Nov 27 '15 at 13:32
  • Yes, i mean compound assignment. ``short`` is not the data type, sorry. – raiserle Nov 27 '15 at 15:27
  • Using a cast in `a = (uint8_t)(a|b)` is not a "requirement by the standard". According to the standard, this is exactly identical to `a |= b`. – M.M Nov 27 '15 at 21:35
  • Also there is no such thing as *default integer conversions*. – M.M Nov 27 '15 at 21:37
  • @M.M: It is not **exactly** identical, but equivalent with some [exceptions](http://port70.net/~nsz/c/c11/n1570.html#6.5.16.2p3). For the "default integer conversions": You are right, there is no such term. The correct prase would be _integer promotions_ which are part of the _usual arithmetic conversions_. Edited. But the intention should be clear. – too honest for this site Nov 28 '15 at 07:17
  • @M.M: The part about "requirement by the standard was badly worded. – too honest for this site Nov 28 '15 at 07:46