2013-02-01 03:05 AM
Hi.
I'm using the Cosmic STM8 compiler (''free'' version, v4.3.9) and it appears to generate unexpected results for the following test :- static char test(unsigned int x, unsigned int y) { return (int)(x - y) < 0; } This function unexpectedly returns 0 when presented with the values x=0x7f80 and y=0x8080. If I modify the function thus is then returns 1 as expected :- static char test(unsigned int x, unsigned int y) { x -= y; return (int) x < 0; } Am I misunderstanding what the result should be ? P.2013-02-06 06:20 AM
Apologies if this a stupid remark, but it is not clear to me why the int cast is only done after the subtraction.
To me, the result cannot be negative because it is an unsigned value, so it is wrapped, and then you cast it to int and because MSB=1 you get a negative value. If I understand the purpose of the code, I would write return( ((int) x) - ((int) y) )<02013-02-08 01:20 AM
Hi,
I think, we all agree, that it is the proper way and good style programming to cast before subtracting.return ( ( (signed int)x - (signed int)y ) < 0 );
BTW: Even an int defined as 32bit or even 64bit word would bring the same result without casting previously to the subtract operation. You only must not mix types as short, long and chars in one operation. 0x00007F80 - 0x00008080 = 0xFFFFFF00 -> cast (signed int) ->
negative
There is a file at the STM8S Discovery examples called stm8s_type.h, containing the following typedefs:
typedef signed long s32;
typedef signed short s16;
typedef signed char s8;
typedef unsigned long u32;
typedef unsigned short u16;
typedef unsigned char u8;
I really like to use this short form and then to write
return( ((s16)x - (s16)y) < 0 );
is quite convenient ;o). WoRo
2013-02-08 03:36 AM
I don't think we all agree.
Both ''x'' and ''y'' are unsigned quantities, they just happen to wrap at the 16-bit boundary. If you consider them as values read from a counter for example, they are unsigned values but the difference between two counts can be positive or negative depending on whether one count leads or lags the other.2013-02-08 03:39 AM
We don't all agree.
If, for example, ''x'' and ''y'' are values read from a counter timer they are intrinsically unsigned quantities, they just happen to wrap at 16 bits. Though the values are unsigned the difference between two counter timer values can be signed and the result should be correct as long as the values don't differ by more than 15 bits.2013-02-08 03:42 AM
I've attached a file which includes generated assembly etc.
Fundamentally the compiler generates different code for these two cases, which is an error as they are functionally identical. void delta_1(unsigned int x, unsigned int y) { return (int)(x - y) < 0; } void delta_2(unsigned int x, unsigned int y) { x -= y; return (int) x < 0; } ________________ Attachments : details.txt : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006HzOJ&d=%2Fa%2F0X0000000bKz%2FxG1xu8aBxSZ7h1tbwsj5x485ST4hk0lubyYVxKhKRwI&asPdf=false2013-02-08 05:31 AM
Hello Peter.
The different behaviour between delta_1 and delta_2 is due to the instruction: 709 80cf 2e05 jrsge L4 This instruction executes a jump when (N XOR V) = 0, where N and V are respectively the negative flag and the overflow flag. When x = 0x7F80 and y = 0x8080, both N and V are set, so the jump is taken. The cast operation is the culprit of this (mis)behaviour, because a signed int can't hold the value of y. You may wonder if this behaviour is a compiler bug, or else. I suppose that it's ''implementation defined'', so it's a potential source of errors as all the things that are related to the cast operator. As regards the subtraction of counter values, which are unsigned quantities by definition, their difference is still the elapsed time, even if y is greater than x. When x = 0x7F80 and y = 0x8080, their difference is 0xF800 (unsigned) which is exactly the number of counter ticks that elapsed between the ending tick (x) and the starting tick (y). This result holds only if the counter overflows at most once, otherwise the result must be adjusted with the product of the counter resolution and the number of overflows minus one. EtaPhi2013-02-08 06:47 AM
But the code for delta_1() and delta_2() are the same. Both feature an unsigned subtraction followed by a cast to int and a comparison with 0. The compiler should ideally generate the same code for both cases, failing that it should at least generate the same result.
2013-02-08 07:36 AM
An ideal compiler would also remove the instructions:
704 80cb 89 pushw x
716 80d7 5b02 addw sp,#2
because they are useless. The Cosmic compiler does its best to help you, but it sometimes can't translate what you mean because of C language ambiguities whose behaviour is ... implementation defined! If you want a certain kind of result, you must avoid ambiguities, otherwise you must use the assembler language...2013-02-08 08:29 AM
I tried the different codes with another compiler and there I get the following:
For all the four lines { return (int)(x-y) < 0; }{ return ((int)x - (int)y) < 0; }
{ x -= y; return (int)x < 0; }
{ int z = x - y; return z < 0;}
the compiler produces absolutely identical codes, similar to what we get with the 3rd line. While the lines { return x < y; }
{ return (int)x < (int)y; }
will produce code, where the results depend on the signed or unsigned comparison. I think, Peter you found something very special. ????? WoRo