0

I was surprised why I get different results.

    uint16_t wCaptureTime = 35;

    wDeltaTime1 = (uint16_t) - (int16_t)wCaptureTime;
    wDeltaTime2 =  0xFFFF - (int16_t)wCaptureTime;

    printf("wDeltaTime1 %d \n", wDeltaTime1);
    printf("wDeltaTime2 %d \n", wDeltaTime2);

The code results in:

wDeltaTime1 65501 
wDeltaTime2 65500 

Could someone explain or give some link to read about that behaviour.

4 Answers4

6

The (uint16_t) in the expression (uint16_t) - (int16_t)wCaptureTime is a cast. When -35 is cast to uint16_t you get -35 + 65536 which is 65501.

If sizeof(int) is 2 then 0xFFFF - (int16_t)wCaptureTime is 65535 - 35 in unsigned arithmetic which is 65500. If sizeof(int) is 4 then you'll get the same result, although the expression will be evaluated in signed (int) arithmetic.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
3

Supposing that the implementation uses 32-bit int, and wDeltaTime1 is an int, then the operations in DeltaTime1 = (uint16_t) - (int16_t)wCaptureTime; are:

  • The (int16_t) cast converts the uint16_t wCaptureTime to int16_t. The value remains 35.
  • This int16_t is automatically promoted to int. The value remains 35.
  • The unary - operator negates the value, producing −35.
  • The (uint16_t) cast converts it to uint16_t. Conversion to uint16_t is performed modulo 65536, so the result is −35 + 65536 = 65501.
  • This uint16_t converted to the type of wDeltaTime1, int. The value is 65501.
  • This value is assigned to wDeltaTime1, producing 65501 in wDeltaTime1.

The operations in wDeltaTime2 = 0xFFFF - (int16_t)wCaptureTime; are:

  • 0xFFFF is an int constant with value 65535.
  • The (int16_t) cast converts the uint16_t wCaptureTime to int16_t. The value remains 35.
  • This int16_t is automatically promoted to int. The value remains 35.
  • The binary - operator subtracts 35 from 65535, producing 65535 − 35 = 65500.
  • This value is assigned to wDeltaTime2, producing 65500 in wDeltaTime2.

One source of confusion you may have is that negation in uint16_t is equivalent to subtracting from 65536, not 65535 (or 0xFFFF). 65535 is the “all ones” pattern in 16 bits; it does not act like zero. 65536 is where uint16_t wraps around, so it acts more like zero.

The bitwise complement operator, ~, would change (considering just the low 16 bits) 35 to 65500.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
2

In the first case, you are casting -35 to uint16_t. This is 65501:

- (uint16_t) 35 = 65501 (as an uint16_t)

In the second case, the first operand is 0xffff (65535) so you are getting 65535 - 35 = 65550:

65535 - (uint16_t) 35 = 65500
Grodriguez
  • 21,501
  • 10
  • 63
  • 107
  • 1
    No, there are no implicit operands in C. – John Bollinger Sep 02 '19 at 14:02
  • @JohnBollinger: I think he means `-x` is implicitly `0-x`, not that I agree with that presentation. – Eric Postpischil Sep 02 '19 at 14:03
  • @EricPostpischil, there can be an implicit first operand only if the operator is interpreted as *binary* `-`, whereas of course it is *unary* `-`. That the result is algebraically equivalent to subtracting the single operand from 0 is a weak leg to stand on, in part because algebraic equivalence does always confer computational equivalence, and in part because the two operators have different precedence. – John Bollinger Sep 02 '19 at 14:18
  • 1
    @JohnBollinger: I am not speaking of the C operators. Mathematically, −x is 0-x. – Eric Postpischil Sep 02 '19 at 14:27
  • This answer is misleading. In the second case, the first operand is `0xFFFF`, which will fit inside an `int` or otherwise an `unsigned int`. It will always be a positive value and never get treated as `-1` on any system, unless explicitly cast to a signed type. – Lundin Sep 02 '19 at 14:38
1

Line 1:

  • (int16_t)wCaptureTime; triggers an explicit conversion to int16_t. The value 35 is preserved.
  • -(int16_t)wCaptureTime; applies the unary - operator. In case of 32 bit or larger computer, wCaptureTime is integer promoted to int. In case of 8/16 bit computers, the type remains int16_t .
  • (uint16_t)-(int16_t)wCaptureTime; triggers an explicit conversion of the negative value to uint16_t. This conversion happens as per C17 6.3.1.3:

    Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.

    On a 2's complement 32 bit computer -35 will yield 0xFFDD = 65501.

Line 2:

  • 0xFFFF is of type unsigned int on a 8/16 bit CPU but of type int on a 32 or larger bit CPU.
  • In case 0xFFFF is unsigned int, then the operand (int16_t)wCaptureTime; will get promoted to unsigned int too (regardless of the cast) - because of "the usual arithmetic conversions". In which case the same signed to unsigned conversion as in line 1 occurs and you end up with something like unsigned arithmetic 65535 - 0xFFDD = 34 decimal.
  • In case 0xFFFF is int, then the operand (int16_t)wCaptureTime; will get promoted to int. The operation is simply carried out with two int operands, 65535 - 35 = 65500.

Study two's complement and implicit type promotion rules.

Also, the types of integer contant matters. Most often they are int but in case of hex integer constants, they can also be unsigned int depending on value.

Lundin
  • 195,001
  • 40
  • 254
  • 396