1

I feel like the bloodiest beginner - Why does the following not work:

// declarations
unsigned short currentAddr= 0x0000;
unsigned short addr[20] = {1, 0};

// main 
addr[1] = (~currentAddr)/2+1;
printf("addr[1] wert: %hu\n", addr[1]); // equals 1, expecte 0x8000
addr[1] = ~currentAddr>>1;
printf("addr[1] wert: %hu\n", addr[1]); // equals 65535, expected 0x7FFF

In printf and also in my debugger's watchlist the value for addr[1] is not as expected. My aim is to have half the maximum of the variable, here 0x8000. Info: I am doing ~currentAddr to get the max. 0xFFFF in case short is in a different length on my embedded platform than here on my PC.

cheers, Stefan

Stefatronik
  • 312
  • 2
  • 16
  • Is your purpose to have only set the MSB of the variable? – JFMR Nov 29 '17 at 11:19
  • 3
    So debug it. What do you expect `~currentAddr` to be? What actually is it? What about `~currentAddr/2`? What about that `+1`? – Useless Nov 29 '17 at 11:23
  • I think the problem here is that literal integers (like `2` and `1` in `(~currentAddr)/2+1` are treated as `int` values. As a result, `currentAddr` is being promoted to an `int` before the calculation is done. For some reason, [C doesn't support literal `unsigned short` values](https://stackoverflow.com/questions/1906917/short-int-literals-in-c). – r3mainer Nov 29 '17 at 11:27
  • 2
    The key is that `~currentAddr` evaluates to `~(int)currentaAddr` on machines where `int` is larger than `short`. Note that the result would be `0x8000` on a platform where `sizeof (int) == sizeof (short)` because then the expression would be evaluated as `unsigned`. – Klas Lindbäck Nov 29 '17 at 11:32
  • 1
    Simply insure unsigned division. Use `addr[1] = (~currentAddr)/2u+1;` (add u) – chux - Reinstate Monica Nov 29 '17 at 14:31

4 Answers4

6

What went wrong

The integer promotions are performed on the operand of the unary ~.

On many systems int is larger than short. On such systems, for unsigned short currentAddr = 0, the value of currentAddr is first promoted to int in the expression ~currentAddr. Then ~currentAddr evaluates to -1 (assuming twos-complement representation).

On some systems int and short may be the same size (though int must be at least as large as short); here currentAddr would instead be promoted to unsigned int since an int cannot hold all values of an unsigned integer type of the same size. In such a case, ~currentAddr would evaluate to UINT_MAX. For 16-bit int (short must be at least 16-bit, so here int and short would be the same size) the result of ~currentAddr would be 65,535.

The OP's system must have int larger than short. In the case of addr[1] = (~currentAddr)/2+1; this becomes addr[1] = (-1)/2+1; which evaluates to 1.

In the second case, addr[1] = ~currentAddr>>1; evaluates to addr[1] = (-1)>>1;. Here, the result of right-shifting a negative value is implementation-defined. In the present case, the result appears to be INT_MAX, which is converted to unsigned short in the assignment to addr[1], which takes the value USHRT_MAX in the conversion. This value is 65,535 on OP's system.

What to do about it

To obtain maximum and minimum values for standard integer types clearly and reliably, use the macros found in limits.h instead of attempting bit manipulations. This method will not disappoint:

#include <stdio.h>
#include <limits.h>

int main(void)
{
    unsigned short val;

    val = (USHRT_MAX / 2) + 1;
    printf("(USHRT_MAX / 2) + 1: %#hx\n", val);

    val = USHRT_MAX >> 1;
    printf("     USHRT_MAX >> 1: %#hx\n", val);

    return 0;
}

Program output:

(USHRT_MAX / 2) + 1: 0x8000
     USHRT_MAX >> 1: 0x7fff
ad absurdum
  • 19,498
  • 5
  • 37
  • 60
  • Hello David, I tried following: `unsigned short c;` `c = ~currentAddr;` `unsigned short c_z = sizeof (c);`` `unsigned short ci;` `ci = (c >> 1)+1;` `unsigned short ci_z = sizeof (ci);` `addr[1] =ci; ` (btw. how do I make a \n in the comment?) Now everything is fine. But if what you said is true, shouldn't I have an int in my second line? – Stefatronik Nov 29 '17 at 13:49
  • I meant shouldn't c bekome an int in line 2... Sorry I am having trouble with the comment feature and the formatting options... – Stefatronik Nov 29 '17 at 13:55
  • Note: "`~currentAddr` evaluates to -1" is also implementation-defined - yet very commonly the result is -1 on 32-bit `int`. On 16-bit `int/unsigned` (common in embedded world), `~currentAddr` would be 65,535. – chux - Reinstate Monica Nov 29 '17 at 14:17
  • @DavidBowling On 16-bit `int/unsigned`, `unsigned short` promoted to `unsigned` where -1u is 65,535. Detailed [here](https://stackoverflow.com/a/47555535/2410359). – chux - Reinstate Monica Nov 29 '17 at 14:58
  • @Stefatronik -- not quite sure I follow you; with `unsigned short c; c = ~currentAddr;`, `currentAddr` is promoted to `int` (or `unsigned int` in some cases). Then `~currentAddr` evaluates to `-1` for the `int` case. When the assignment is made to `unsigned short c` the value `-1` undergoes conversion to `unsigned short`, yielding `USHRT_MAX`. – ad absurdum Nov 29 '17 at 15:02
  • 1
    Recommend we delete our paired comments. – chux - Reinstate Monica Nov 29 '17 at 16:01
1

The problem lies here:

addr[1] = (~currentAddr)/2+1;

You expect currentAddr to 0xFFFF, which is partially right. But, what you might have missed out is integer promotion rule, which makes it 0xFFFFFFFF which is hexadecimal representation of -1.

Now, there is simple math: (~currentAddr)/2+1 is nothing but 0x01 or 1, When you ~currentAddr>>1; do this shift, it is again becoming -1.

From

My aim is to have half the maximum of the variable, here 0x8000

If I understand you correctly, what you are trying to do is get the value which is equal to (Maximum value of Unsigned short)/2. If it is so, the proper way of doing it will be using USHRT_MAX. Of course, you'll need to include limits.h file in your source code.


Update:

Referring to your comments to David's answer, following changes works as expected. (You have tested, I haven't)

unsigned short c; 
c = ~currentAddr; 
unsigned short c_z = sizeof (c);
unsigned short ci; 
ci = (c >> 1) + 1; 
unsigned short ci_z = sizeof (ci); 
addr[1] = ci;

Now, why this isn't promoted to integer as opposed to previous case

c = ~currentAddr; 

It is promoted, but it yields an expected result because, as chux explainned (which I couldn't have done) it is (temporarily) promoted to int during its operation, but resolved as (converted to) a unsigned short again when it is stored in memory allocated to c.

The C standard answers the question:

From the C99 standard: 6.5.16.1 Simple assignment

In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand. In your case since both the LHS and RHS are of the same type, there is no need for any conversion.

Also, it says:

The type of an assignment expression is the type the left operand would have after lvalue conversion.

The same is specified by C11 6.5.16.1/2:

In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.

Try this yourself:

int main(void)
{
    unsigned short c; 
    unsigned short currentAddr= 0x0000;
    c = ~currentAddr;

    printf("\n0x%x", c);
    printf("\n0x%x", (~currentAddr));

    return 0;
}

This should print:

0xffff
0xffffffff
WedaPashi
  • 3,561
  • 26
  • 42
  • Hey. Pls see my reply to David Bowling. I have a separate line doing `unsigned short c = ~currentAddr;` that works - I mean c remains short. Where is the difference? – Stefatronik Nov 29 '17 at 13:58
  • "Now, why this isn't promoted to integer as opposed to previous case `c = ~currentAddr;`" mis-leads. `currentAddr` is still promoted to `int` on 32-bit machines before applying the `~`. That operation results in an `int`. The `int` value is then saved in `c`, yet then goes through a conversion to `unsigned short`. – chux - Reinstate Monica Nov 29 '17 at 14:26
  • 1
    @chux: Oh, honestly I didn't see that coming. Thanks for explaining. I have updated the answer accordingly. – WedaPashi Nov 29 '17 at 14:31
  • @WedaPashi Thanks for the update - this was the jumping point. So I better expect C to do most operations in standard int. The assignment helps me to make it _the other_ datatype. Doing operations with the unexpected type may / will fail like I have proven in my initial post ^^ Thanks – Stefatronik Nov 29 '17 at 14:53
  • @Stefatronik: Welcome! If we (David, chux and I) have answered your question then may be You should accept one of the answers. – WedaPashi Nov 29 '17 at 16:50
1
addr[1] = (~currentAddr)/2+1;

Let us break it down: currentAddr is an unsigned short involved in a computation so the value/type is first promoted to int or unsigned. In C this is integer promotion.

If an int can represent all values of the original type ..., the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions. C11dr §6.3.1.1 2


When USHRT_MAX <= INT_MAX, (e.g. 16 bit short int/unsigned, 32-bit int/unsigned), code is like below. With currentAddr == 0 and typical 2's complement behavior, ~0 --> -1 and addr[1] --> 1.

int tmp = currentAddr;
addr[1] = (~tmp)/2+1;  

When USHRT_MAX > INT_MAX, (e.g. 16 bit short int/unsigned, 16-bit int/unsigned), code is like below. With currentAddr == 0 and unsigned behavior, ~0 --> 0xFFFF and addr[1] --> 0x8000.

unsigned tmp = currentAddr;
addr[1] = (~tmp)/2+1;  

My aim is to have half the maximum of the variable

The best way to get the maximum of an unsigned short is to use SHRT_MAX and skip the ~ code. It will work as expected regardless of unsigned short, int, unsigned range. It also better documents code intent.

#include <limits.h>
addr[1] = USHRT_MAX/2+1;
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • @Stefatronik pre-C11, for user code to discern the maximum of a variable of _arbitrary type_ is not possible with risking UB unless 1) the type is known to be some unsigned type and max value is known to be >= `UINT_MAX` or 2) the `sizeof(var) == 1`. With C11, `_Generic` can be used to find the max of select types. – chux - Reinstate Monica Nov 29 '17 at 18:28
0

Because the number 2 is int and int can hold unsigned short,so,the the actual operation is addr[1] = (unsigned short)(((int)(~currentAddr)/2)+1)

msc
  • 33,420
  • 29
  • 119
  • 214
ABC CHN
  • 21
  • 1
  • This would be a better answer if you included a workaround to the OP's problem. – r3mainer Nov 29 '17 at 11:28
  • You might add an explanation why this is a problem. It should work fine unless already `(~currentAddr)` has more bits than `unsigned short`, i.e. `currentAddr` is promoted before applying `~` operator. – Gerhardh Nov 29 '17 at 11:35
  • Well,I tried in my VS, and found the result is actually decided by the type of value X/2 (because 2 is int and int can hold unsinged short,so result is int ),so you can write like this addr[1] = (unsigned short)(~currentAddr)/2+1; – ABC CHN Nov 29 '17 at 11:57
  • The problem is that already `~` causes promotion. If the division by 2 was the reason for promotion, it would be `((unsigned short)0xFFFF)/2`which promotes to `(int)0x0000FFFF/2` resulting to `0x00007FFF` as expected – Gerhardh Nov 29 '17 at 12:04
  • Some things happened....As you see,(type)(~)/int,lets see the operator priority about this equation,'(~)'>'(type)'>'/'.this commnent refutes my answer and last comment...... – ABC CHN Nov 29 '17 at 12:13
  • `addr[1] = ((unsigned short)~currentAddr)/2+1; ` seems to work. I wonder why I hadn't tried any cast before? So Gerhardh looks like you are right with you answer – Stefatronik Nov 29 '17 at 14:26
  • 1
    @Stefatronik Avoid casting as in `((unsigned short)~currentAddr)/2+1;`. This may "work" for this C code, yet casting in a computation is usually a sign of other problems and better fixes exist. – chux - Reinstate Monica Nov 29 '17 at 15:06
  • @chux Yes you're right- I implemented the WedaPashi's suggestion `addr[1] = USHRT_MAX/2+1;`... Good discussion here thanks to all proposals, I learned a lot! – Stefatronik Nov 30 '17 at 08:22