EmbeddedRelated.com
Forums
The 2026 Embedded Online Conference

C++ problem

Started by tim... March 1, 2016
On 02/03/16 23:23, Dimiter_Popoff wrote:
> On 02.3.2016 г. 23:19, David Brown wrote: >> On 02/03/16 15:59, Dimiter_Popoff wrote: >>> On 02.3.2016 г. 16:50, David Brown wrote: >>>> On 02/03/16 15:08, Dimiter_Popoff wrote: >>>>> On 02.3.2016 г. 15:17, raimond.dragomir@gmail.com wrote: >>>>>> miercuri, 2 martie 2016, 14:45:34 UTC+2, David Brown a scris: >>>>>>> On 02/03/16 11:50, raimond.dragomir@gmail.com wrote: >>>>>>>>> int32_t a1 = (int32_t) a; >>>>>>>>> int32_t b1 = (int32_t) b; >>>>>>>>> int32_t res1 = a1 * b1; >>>>>>>>> >>>>>>>>> If this is called with a and b equal to 0xffff, the 32-bit signed >>>>>>>>> multiplication will overflow - it is undefined behaviour. >>>>>>>>> >>>>>>>> Since you want only 32bit of the result, this is correct. You get >>>>>>>> incorrect >>>>>>>> results only if you use full multiplication (64bit result) with >>>>>>>> different >>>>>>>> unsigned/signed operation/result. >>>>>>> >>>>>>> No, it is not correct - it is undefined behaviour. As signed 32-bit >>>>>>> ints, 0xffff * 0xffff would be an overflow, and therefore undefined >>>>>>> behaviour. Operations like that are not defined as nice two's >>>>>>> complement arithmetic in C (even though in practice, that is what >>>>>>> the >>>>>>> compiler would probably generate). >>>>>>> >>>>>>>> >>>>>>>> When the result of a multiplication has the same size as the >>>>>>>> operands, the >>>>>>>> result is correct regardless signed/unsigned of any operands, >>>>>>>> including the >>>>>>>> result. This may be counter-intuitive unless you know what you are >>>>>>>> doing. >>>>>>>> You must remember that a multiplication always gives a double >>>>>>>> product, so >>>>>>>> taking only the low part gives you ... the low part. >>>>>>> >>>>>>> It has nothing to do with that (I understand how multiplication >>>>>>> works in >>>>>>> this context, though I appreciate that not everyone does). The >>>>>>> point to >>>>>>> be aware of here is that operations on unsigned types smaller than >>>>>>> "int" >>>>>>> are promoted to /signed/ int before the operation is carried out. >>>>>>> That >>>>>>> can mean you get unexpected undefined behaviour due to /signed/ >>>>>>> overflows, even though /unsigned/ overflow is fully defined in C >>>>>>> (and >>>>>>> C++). >>>>>>> >>>>>>> The point is subtle, and it is unlikely to be an issue in >>>>>>> practice. But >>>>>>> I think it is worth mentioning these details. >>>>>> >>>>>> Sorry, maybe I don't understand your point. I basically said that >>>>>> taking the >>>>>> low part gives correct result. In the very example given, 0xffff >>>>>> (16bit initially) >>>>>> * 0xffff (16bit) gives the 0x0001 result (16bit). What 16bit result >>>>>> would >>>>>> you expect? >>>>>> This result is correct in both cases (a 16bit operation (if >>>>>> available) >>>>>> and a converted operation with signed 32bit integers). >>>>>> >>>>>> Can you give an example where the conversion of an unsigned to the >>>>>> bigger >>>>>> signed type and then the multiplied result of the signed numbers >>>>>> truncated >>>>>> back is wrong? >>>>>> >>>>>> >>>>> >>>>> I suppose he means that you do get the correct result by ignoring the >>>>> overflow, which after all is not the correct result. >>>>> >>>>> $ffff unsigned expands to 32 bit $0000ffff signed, $ffff signed >>>>> expands to $ffffffff signed. The signed product of these is $ffff0001, >>>>> which does not fit in 16 bits, it takes 17 bits at least. >>>>> Hardly a huge issue if one knows what he is doing, >>>>> but if you rely on a compiler to know these things for you it can't >>>>> be just dismissed. >>>>> >>>> >>>> No, the issue is that the signed product 0xffffffff does not fit in a >>>> signed 32-bit integer - and that is undefined behaviour. This >>>> intermediary value exists logically before the conversion to 16-bit >>>> unsigned by modulo arithmetic. >>>> >>>> >>> >>> David, the product is not $ffffffff - how did you arrive at that? >>> >> >> I used a little-known trick - the typo! The issue is that the signed >> product 0xffff0001 does not fit in a signed 32-bit integer, causing >> undefined behaviour. >> >> Sorry for the confusion. >> >> > > Uhm, so the "No" must also have been a typo ;). > > BTW the $ffff0001 does fit in a signed 32 bit integer, it does not > fit in 16 bits (fits in 17 bits). >
No, the "no" was not a typo - and no, 0xffff0001 does not fit in a signed 32-bit integer. The range for a 32-bit signed integer is -0x80000000 to +0x7fffffff. 0xffff0001 is outside of that range, and cannot be represented.
David Brown <david.brown@hesbynett.no> writes:
> No, the "no" was not a typo - and no, 0xffff0001 does not fit in a > signed 32-bit integer.
I think it's 0xfffe0001, but yeah, that is 4294901761 decimal, which is not representable as a signed int32.
On 3/2/2016 4:18 PM, Hans-Bernhard Br&#4294967295;ker wrote:
> Am 02.03.2016 um 00:50 schrieb rickman: >> On 3/1/2016 5:20 PM, Hans-Bernhard Br&#4294967295;ker wrote: >>> Am 01.03.2016 um 22:02 schrieb tim...: > >>>> But I did get rid of it by using a c-style cast to make the unsigned >>>> int a signed one when comparing, and that suppressed the warning, but >>>> it still didn't work. - not until I redefined my const as signed! > >>> That attempt most probably failed in some other, unrelated way. Which >>> way it was can only be found out if you show an actual, essentially >>> complete (as in: compilable) example case. >> >> That's easy. He said he was counting down to -1 which won't work if the >> value is cast to an unsigned. > > No, it's not that easy, because per his actual statement (see above) he > did _not_ cast the signed -1 to unsigned in this try; he said he cast > the const unsigned 0 to signed, instead. That should have worked.
Yes, I have it backwards. So why didn't that work? -- Rick
On 03.3.2016 &#1075;. 00:38, David Brown wrote:
> On 02/03/16 23:23, Dimiter_Popoff wrote: >> On 02.3.2016 &#1075;. 23:19, David Brown wrote: >>> On 02/03/16 15:59, Dimiter_Popoff wrote: >>>> On 02.3.2016 &#1075;. 16:50, David Brown wrote: >>>>> On 02/03/16 15:08, Dimiter_Popoff wrote: >>>>>> On 02.3.2016 &#1075;. 15:17, raimond.dragomir@gmail.com wrote: >>>>>>> miercuri, 2 martie 2016, 14:45:34 UTC+2, David Brown a scris: >>>>>>>> On 02/03/16 11:50, raimond.dragomir@gmail.com wrote: >>>>>>>>>> int32_t a1 = (int32_t) a; >>>>>>>>>> int32_t b1 = (int32_t) b; >>>>>>>>>> int32_t res1 = a1 * b1; >>>>>>>>>> >>>>>>>>>> If this is called with a and b equal to 0xffff, the 32-bit signed >>>>>>>>>> multiplication will overflow - it is undefined behaviour. >>>>>>>>>> >>>>>>>>> Since you want only 32bit of the result, this is correct. You get >>>>>>>>> incorrect >>>>>>>>> results only if you use full multiplication (64bit result) with >>>>>>>>> different >>>>>>>>> unsigned/signed operation/result. >>>>>>>> >>>>>>>> No, it is not correct - it is undefined behaviour. As signed >>>>>>>> 32-bit >>>>>>>> ints, 0xffff * 0xffff would be an overflow, and therefore undefined >>>>>>>> behaviour. Operations like that are not defined as nice two's >>>>>>>> complement arithmetic in C (even though in practice, that is what >>>>>>>> the >>>>>>>> compiler would probably generate). >>>>>>>> >>>>>>>>> >>>>>>>>> When the result of a multiplication has the same size as the >>>>>>>>> operands, the >>>>>>>>> result is correct regardless signed/unsigned of any operands, >>>>>>>>> including the >>>>>>>>> result. This may be counter-intuitive unless you know what you are >>>>>>>>> doing. >>>>>>>>> You must remember that a multiplication always gives a double >>>>>>>>> product, so >>>>>>>>> taking only the low part gives you ... the low part. >>>>>>>> >>>>>>>> It has nothing to do with that (I understand how multiplication >>>>>>>> works in >>>>>>>> this context, though I appreciate that not everyone does). The >>>>>>>> point to >>>>>>>> be aware of here is that operations on unsigned types smaller than >>>>>>>> "int" >>>>>>>> are promoted to /signed/ int before the operation is carried out. >>>>>>>> That >>>>>>>> can mean you get unexpected undefined behaviour due to /signed/ >>>>>>>> overflows, even though /unsigned/ overflow is fully defined in C >>>>>>>> (and >>>>>>>> C++). >>>>>>>> >>>>>>>> The point is subtle, and it is unlikely to be an issue in >>>>>>>> practice. But >>>>>>>> I think it is worth mentioning these details. >>>>>>> >>>>>>> Sorry, maybe I don't understand your point. I basically said that >>>>>>> taking the >>>>>>> low part gives correct result. In the very example given, 0xffff >>>>>>> (16bit initially) >>>>>>> * 0xffff (16bit) gives the 0x0001 result (16bit). What 16bit result >>>>>>> would >>>>>>> you expect? >>>>>>> This result is correct in both cases (a 16bit operation (if >>>>>>> available) >>>>>>> and a converted operation with signed 32bit integers). >>>>>>> >>>>>>> Can you give an example where the conversion of an unsigned to the >>>>>>> bigger >>>>>>> signed type and then the multiplied result of the signed numbers >>>>>>> truncated >>>>>>> back is wrong? >>>>>>> >>>>>>> >>>>>> >>>>>> I suppose he means that you do get the correct result by ignoring the >>>>>> overflow, which after all is not the correct result. >>>>>> >>>>>> $ffff unsigned expands to 32 bit $0000ffff signed, $ffff signed >>>>>> expands to $ffffffff signed. The signed product of these is >>>>>> $ffff0001, >>>>>> which does not fit in 16 bits, it takes 17 bits at least. >>>>>> Hardly a huge issue if one knows what he is doing, >>>>>> but if you rely on a compiler to know these things for you it can't >>>>>> be just dismissed. >>>>>> >>>>> >>>>> No, the issue is that the signed product 0xffffffff does not fit in a >>>>> signed 32-bit integer - and that is undefined behaviour. This >>>>> intermediary value exists logically before the conversion to 16-bit >>>>> unsigned by modulo arithmetic. >>>>> >>>>> >>>> >>>> David, the product is not $ffffffff - how did you arrive at that? >>>> >>> >>> I used a little-known trick - the typo! The issue is that the signed >>> product 0xffff0001 does not fit in a signed 32-bit integer, causing >>> undefined behaviour. >>> >>> Sorry for the confusion. >>> >>> >> >> Uhm, so the "No" must also have been a typo ;). >> >> BTW the $ffff0001 does fit in a signed 32 bit integer, it does not >> fit in 16 bits (fits in 17 bits). >> > > No, the "no" was not a typo - and no, 0xffff0001 does not fit in a > signed 32-bit integer. The range for a 32-bit signed integer is > -0x80000000 to +0x7fffffff. 0xffff0001 is outside of that range, and > cannot be represented. > >
LOL, David. So you have yet to learn that the signed product of (-1)*(65535) (signed $ffff 16 bit * unsigned $ffff 16 bit) is -65535, representable in 32 bits as $ffff0001, does fit in a minimum of 17 bits and of course does fit in 32 bits. How long did you say you had been working as a programmer? :D Dimiter
rickman wrote:
> > On 3/2/2016 4:18 PM, Hans-Bernhard Br&#4294967295;ker wrote: > > Am 02.03.2016 um 00:50 schrieb rickman: > >> On 3/1/2016 5:20 PM, Hans-Bernhard Br&#4294967295;ker wrote: > >>> Am 01.03.2016 um 22:02 schrieb tim...: > > > >>>> But I did get rid of it by using a c-style cast to make the unsigned > >>>> int a signed one when comparing, and that suppressed the warning, but > >>>> it still didn't work. - not until I redefined my const as signed! > > > >>> That attempt most probably failed in some other, unrelated way. Which > >>> way it was can only be found out if you show an actual, essentially > >>> complete (as in: compilable) example case. > >> > >> That's easy. He said he was counting down to -1 which won't work if the > >> value is cast to an unsigned. > > > > No, it's not that easy, because per his actual statement (see above) he > > did _not_ cast the signed -1 to unsigned in this try; he said he cast > > the const unsigned 0 to signed, instead. That should have worked. > > Yes, I have it backwards. So why didn't that work?
I'm sure we could easily figure it out, but only if he shows us the code.
On 3/2/2016 10:37 AM, Phil Hobbs wrote:
> > Since essentially all computers still crunching use twos-complement binary > for signed ints, that behaviour is very nearly guaranteed. IIRC you can > turn on integer overflow traps so that it behaves like a divide-by-zero, > but a whole lot of software relies on this behaviour.
I don't believe in utilizing such behavior. The language has typing for a reason and ignoring it can get you into trouble as happened here.
> Yup. A copy of PC-Lint is helpful too (assuming they've kept it up to > date--my copy doesn't understand anything past C++08.)
Why would anyone need PC-Lint? The compiler gave a warning about mixing of data types. It has been awhile since I programmed much in C, but when I did I always programmed for zero warnings. I should do that in VHDL for FPGAs too, but oddly enough, some of the warnings come in the steps that are vendor dependent and warnings are generated by the things the tools do with no way to prevent the warnings other than turning off reporting of warnings. Stupid vendors! -- Rick
Phil Hobbs <pcdhSpamMeSenseless@electrooptical.net> writes:
> Since essentially all computers still crunching use twos-complement binary > for signed ints, that behaviour is very nearly guaranteed. IIRC you can > turn on integer overflow traps so that it behaves like a divide-by-zero, > but a whole lot of software relies on this behaviour.
You mean wraparound overflow for signed ints? Gcc has a -fwrapv option to forcet that behaviour, but at that point you're dealing with a special dialect of the language. C specifies that unsigned int overflow is UB to enable compilers to make optimizations that rely on ints not overflowing: http://kristerw.blogspot.com/2016/02/how-undefined-signed-overflow-enables.html If your program expects signed ints to overflow with 2s-complement or any other behaviour, your code is buggy.
On 03/03/16 06:07, Dimiter_Popoff wrote:
> On 03.3.2016 &#1075;. 00:38, David Brown wrote: >> On 02/03/16 23:23, Dimiter_Popoff wrote: >>> On 02.3.2016 &#1075;. 23:19, David Brown wrote: >>>> On 02/03/16 15:59, Dimiter_Popoff wrote: >>>>> On 02.3.2016 &#1075;. 16:50, David Brown wrote: >>>>>> On 02/03/16 15:08, Dimiter_Popoff wrote: >>>>>>> On 02.3.2016 &#1075;. 15:17, raimond.dragomir@gmail.com wrote: >>>>>>>> miercuri, 2 martie 2016, 14:45:34 UTC+2, David Brown a scris: >>>>>>>>> On 02/03/16 11:50, raimond.dragomir@gmail.com wrote: >>>>>>>>>>> int32_t a1 = (int32_t) a; >>>>>>>>>>> int32_t b1 = (int32_t) b; >>>>>>>>>>> int32_t res1 = a1 * b1; >>>>>>>>>>> >>>>>>>>>>> If this is called with a and b equal to 0xffff, the 32-bit >>>>>>>>>>> signed >>>>>>>>>>> multiplication will overflow - it is undefined behaviour. >>>>>>>>>>> >>>>>>>>>> Since you want only 32bit of the result, this is correct. You get >>>>>>>>>> incorrect >>>>>>>>>> results only if you use full multiplication (64bit result) with >>>>>>>>>> different >>>>>>>>>> unsigned/signed operation/result. >>>>>>>>> >>>>>>>>> No, it is not correct - it is undefined behaviour. As signed >>>>>>>>> 32-bit >>>>>>>>> ints, 0xffff * 0xffff would be an overflow, and therefore >>>>>>>>> undefined >>>>>>>>> behaviour. Operations like that are not defined as nice two's >>>>>>>>> complement arithmetic in C (even though in practice, that is what >>>>>>>>> the >>>>>>>>> compiler would probably generate). >>>>>>>>> >>>>>>>>>> >>>>>>>>>> When the result of a multiplication has the same size as the >>>>>>>>>> operands, the >>>>>>>>>> result is correct regardless signed/unsigned of any operands, >>>>>>>>>> including the >>>>>>>>>> result. This may be counter-intuitive unless you know what you >>>>>>>>>> are >>>>>>>>>> doing. >>>>>>>>>> You must remember that a multiplication always gives a double >>>>>>>>>> product, so >>>>>>>>>> taking only the low part gives you ... the low part. >>>>>>>>> >>>>>>>>> It has nothing to do with that (I understand how multiplication >>>>>>>>> works in >>>>>>>>> this context, though I appreciate that not everyone does). The >>>>>>>>> point to >>>>>>>>> be aware of here is that operations on unsigned types smaller than >>>>>>>>> "int" >>>>>>>>> are promoted to /signed/ int before the operation is carried out. >>>>>>>>> That >>>>>>>>> can mean you get unexpected undefined behaviour due to /signed/ >>>>>>>>> overflows, even though /unsigned/ overflow is fully defined in C >>>>>>>>> (and >>>>>>>>> C++). >>>>>>>>> >>>>>>>>> The point is subtle, and it is unlikely to be an issue in >>>>>>>>> practice. But >>>>>>>>> I think it is worth mentioning these details. >>>>>>>> >>>>>>>> Sorry, maybe I don't understand your point. I basically said that >>>>>>>> taking the >>>>>>>> low part gives correct result. In the very example given, 0xffff >>>>>>>> (16bit initially) >>>>>>>> * 0xffff (16bit) gives the 0x0001 result (16bit). What 16bit result >>>>>>>> would >>>>>>>> you expect? >>>>>>>> This result is correct in both cases (a 16bit operation (if >>>>>>>> available) >>>>>>>> and a converted operation with signed 32bit integers). >>>>>>>> >>>>>>>> Can you give an example where the conversion of an unsigned to the >>>>>>>> bigger >>>>>>>> signed type and then the multiplied result of the signed numbers >>>>>>>> truncated >>>>>>>> back is wrong? >>>>>>>> >>>>>>>> >>>>>>> >>>>>>> I suppose he means that you do get the correct result by ignoring >>>>>>> the >>>>>>> overflow, which after all is not the correct result. >>>>>>> >>>>>>> $ffff unsigned expands to 32 bit $0000ffff signed, $ffff signed >>>>>>> expands to $ffffffff signed. The signed product of these is >>>>>>> $ffff0001, >>>>>>> which does not fit in 16 bits, it takes 17 bits at least. >>>>>>> Hardly a huge issue if one knows what he is doing, >>>>>>> but if you rely on a compiler to know these things for you it can't >>>>>>> be just dismissed. >>>>>>> >>>>>> >>>>>> No, the issue is that the signed product 0xffffffff does not fit in a >>>>>> signed 32-bit integer - and that is undefined behaviour. This >>>>>> intermediary value exists logically before the conversion to 16-bit >>>>>> unsigned by modulo arithmetic. >>>>>> >>>>>> >>>>> >>>>> David, the product is not $ffffffff - how did you arrive at that? >>>>> >>>> >>>> I used a little-known trick - the typo! The issue is that the signed >>>> product 0xffff0001 does not fit in a signed 32-bit integer, causing >>>> undefined behaviour. >>>> >>>> Sorry for the confusion. >>>> >>>> >>> >>> Uhm, so the "No" must also have been a typo ;). >>> >>> BTW the $ffff0001 does fit in a signed 32 bit integer, it does not >>> fit in 16 bits (fits in 17 bits). >>> >> >> No, the "no" was not a typo - and no, 0xffff0001 does not fit in a >> signed 32-bit integer. The range for a 32-bit signed integer is >> -0x80000000 to +0x7fffffff. 0xffff0001 is outside of that range, and >> cannot be represented. >> >> > > LOL, David. > So you have yet to learn that the signed product of (-1)*(65535) > (signed $ffff 16 bit * unsigned $ffff 16 bit) is -65535, representable > in 32 bits as $ffff0001, does fit in a minimum of 17 bits and of course > does fit in 32 bits. >
The product of uint16_t 0xffff and uint16_t 0xffff, on a 32-bit int system, is undefined in C. This is because the calculation is done as 32-bit signed, and the value 0xfffe0001 (avoiding typos, I hope) is too big for signed int32_t. The value 0xfffe0001 is 4294901761 in decimal - it is /not/ -65535. Sure, you can fit -65535 into a signed int32_t - or even an int17_t if you like. But you cannot fit 4294901761 into a signed int32_t. If you have 65535 people, and each of them eat 65535 jelly beans, they have eaten 4294901761 jelly beans in total - they have not eaten -65535 beans. Remember, this is /C/ (or C++, they work the same at this level) we are discussing. In C, integers are a little odd. They do not behave as two's complement, do not wrap, and cannot be allowed to overflow. It is not like assembly programming, where 0xffff0001 is the same thing as -65535 with 32-bit registers.
> How long did you say you had been working as a programmer? :D >
I know you have been an assembly programmer since the dawn of time, but the subtleties of C implicit conversions can trip up even the most experienced programmers.
On 03.3.2016 &#1075;. 10:16, David Brown wrote:
> On 03/03/16 06:07, Dimiter_Popoff wrote: >> On 03.3.2016 &#1075;. 00:38, David Brown wrote: >>> On 02/03/16 23:23, Dimiter_Popoff wrote: >>>> On 02.3.2016 &#1075;. 23:19, David Brown wrote: >>>>> On 02/03/16 15:59, Dimiter_Popoff wrote: >>>>>> On 02.3.2016 &#1075;. 16:50, David Brown wrote: >>>>>>> On 02/03/16 15:08, Dimiter_Popoff wrote: >>>>>>>> On 02.3.2016 &#1075;. 15:17, raimond.dragomir@gmail.com wrote: >>>>>>>>> miercuri, 2 martie 2016, 14:45:34 UTC+2, David Brown a scris: >>>>>>>>>> On 02/03/16 11:50, raimond.dragomir@gmail.com wrote: >>>>>>>>>>>> int32_t a1 = (int32_t) a; >>>>>>>>>>>> int32_t b1 = (int32_t) b; >>>>>>>>>>>> int32_t res1 = a1 * b1; >>>>>>>>>>>> >>>>>>>>>>>> If this is called with a and b equal to 0xffff, the 32-bit >>>>>>>>>>>> signed >>>>>>>>>>>> multiplication will overflow - it is undefined behaviour. >>>>>>>>>>>> >>>>>>>>>>> Since you want only 32bit of the result, this is correct. You get >>>>>>>>>>> incorrect >>>>>>>>>>> results only if you use full multiplication (64bit result) with >>>>>>>>>>> different >>>>>>>>>>> unsigned/signed operation/result. >>>>>>>>>> >>>>>>>>>> No, it is not correct - it is undefined behaviour. As signed >>>>>>>>>> 32-bit >>>>>>>>>> ints, 0xffff * 0xffff would be an overflow, and therefore >>>>>>>>>> undefined >>>>>>>>>> behaviour. Operations like that are not defined as nice two's >>>>>>>>>> complement arithmetic in C (even though in practice, that is what >>>>>>>>>> the >>>>>>>>>> compiler would probably generate). >>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>> When the result of a multiplication has the same size as the >>>>>>>>>>> operands, the >>>>>>>>>>> result is correct regardless signed/unsigned of any operands, >>>>>>>>>>> including the >>>>>>>>>>> result. This may be counter-intuitive unless you know what you >>>>>>>>>>> are >>>>>>>>>>> doing. >>>>>>>>>>> You must remember that a multiplication always gives a double >>>>>>>>>>> product, so >>>>>>>>>>> taking only the low part gives you ... the low part. >>>>>>>>>> >>>>>>>>>> It has nothing to do with that (I understand how multiplication >>>>>>>>>> works in >>>>>>>>>> this context, though I appreciate that not everyone does). The >>>>>>>>>> point to >>>>>>>>>> be aware of here is that operations on unsigned types smaller than >>>>>>>>>> "int" >>>>>>>>>> are promoted to /signed/ int before the operation is carried out. >>>>>>>>>> That >>>>>>>>>> can mean you get unexpected undefined behaviour due to /signed/ >>>>>>>>>> overflows, even though /unsigned/ overflow is fully defined in C >>>>>>>>>> (and >>>>>>>>>> C++). >>>>>>>>>> >>>>>>>>>> The point is subtle, and it is unlikely to be an issue in >>>>>>>>>> practice. But >>>>>>>>>> I think it is worth mentioning these details. >>>>>>>>> >>>>>>>>> Sorry, maybe I don't understand your point. I basically said that >>>>>>>>> taking the >>>>>>>>> low part gives correct result. In the very example given, 0xffff >>>>>>>>> (16bit initially) >>>>>>>>> * 0xffff (16bit) gives the 0x0001 result (16bit). What 16bit result >>>>>>>>> would >>>>>>>>> you expect? >>>>>>>>> This result is correct in both cases (a 16bit operation (if >>>>>>>>> available) >>>>>>>>> and a converted operation with signed 32bit integers). >>>>>>>>> >>>>>>>>> Can you give an example where the conversion of an unsigned to the >>>>>>>>> bigger >>>>>>>>> signed type and then the multiplied result of the signed numbers >>>>>>>>> truncated >>>>>>>>> back is wrong? >>>>>>>>> >>>>>>>>> >>>>>>>> >>>>>>>> I suppose he means that you do get the correct result by ignoring >>>>>>>> the >>>>>>>> overflow, which after all is not the correct result. >>>>>>>> >>>>>>>> $ffff unsigned expands to 32 bit $0000ffff signed, $ffff signed >>>>>>>> expands to $ffffffff signed. The signed product of these is >>>>>>>> $ffff0001, >>>>>>>> which does not fit in 16 bits, it takes 17 bits at least. >>>>>>>> Hardly a huge issue if one knows what he is doing, >>>>>>>> but if you rely on a compiler to know these things for you it can't >>>>>>>> be just dismissed. >>>>>>>> >>>>>>> >>>>>>> No, the issue is that the signed product 0xffffffff does not fit in a >>>>>>> signed 32-bit integer - and that is undefined behaviour. This >>>>>>> intermediary value exists logically before the conversion to 16-bit >>>>>>> unsigned by modulo arithmetic. >>>>>>> >>>>>>> >>>>>> >>>>>> David, the product is not $ffffffff - how did you arrive at that? >>>>>> >>>>> >>>>> I used a little-known trick - the typo! The issue is that the signed >>>>> product 0xffff0001 does not fit in a signed 32-bit integer, causing >>>>> undefined behaviour. >>>>> >>>>> Sorry for the confusion. >>>>> >>>>> >>>> >>>> Uhm, so the "No" must also have been a typo ;). >>>> >>>> BTW the $ffff0001 does fit in a signed 32 bit integer, it does not >>>> fit in 16 bits (fits in 17 bits). >>>> >>> >>> No, the "no" was not a typo - and no, 0xffff0001 does not fit in a >>> signed 32-bit integer. The range for a 32-bit signed integer is >>> -0x80000000 to +0x7fffffff. 0xffff0001 is outside of that range, and >>> cannot be represented. >>> >>> >> >> LOL, David. >> So you have yet to learn that the signed product of (-1)*(65535) >> (signed $ffff 16 bit * unsigned $ffff 16 bit) is -65535, representable >> in 32 bits as $ffff0001, does fit in a minimum of 17 bits and of course >> does fit in 32 bits. >> > > The product of uint16_t 0xffff and uint16_t 0xffff, on a 32-bit int > system, is undefined in C. This is because the calculation is done as > 32-bit signed, and the value 0xfffe0001 (avoiding typos, I hope) is too > big for signed int32_t. The value 0xfffe0001 is 4294901761 in decimal - > it is /not/ -65535. Sure, you can fit -65535 into a signed int32_t - or > even an int17_t if you like. But you cannot fit 4294901761 into a > signed int32_t.
David. Will you ever learn to stop digging when in a hole you have put yourself in. The product of SIGNED multiplication (as you repeatedly repeated) is a SIGNED number and $ffff0001 being a SIGNED number is -65535. This is completely independent on language, compiler etc. Just basic arithmetic. Then in NO working compiler would the product of a signed 16 bit being -1 ($ffff) and an unsigned $ffff (65535) - being converted to signed prior to multiplication - will result in a 32 bit overflow, in fact it is quite far from it. IOW, $ffffffff SMUL $0000ffff=$ffff0001 , NO OVERFLOW. It can result in a 16 bit overflow ONLY. The overflow you are seeing is not because the 32 bit product overflows, which it does not. It may be because the unsigned $ffff (65535) overflows when the compiler tries to convert it to a signed 16 bit integer, it does not fit into one. Any properly working compiler wanting the signed 32 bit product we are talking about would sign extend the signed 16 bit to 32, zero extend the unsigned 16 bit to 32 then do the multiplication - in our case the 32 bit result would be about 2^15 far from an overflow. I would have thought this would be obvious also to compiler users, not just writers - evidently not necessarily so.
> If you have 65535 people, and each of them eat 65535 jelly beans, they > have eaten 4294901761 jelly beans in total - they have not eaten -65535 > beans.
I think you must get a 5-th grade arithmetic book and learn about signed numbers, what else can I say to that :D
> Remember, this is /C/ (or C++, they work the same at this level) we are > discussing.
This has nothing to do with C, D or whatever, it is plain arithmetic. Dimiter ------------------------------------------------------ Dimiter Popoff, TGI http://www.tgi-sci.com ------------------------------------------------------ http://www.flickr.com/photos/didi_tgi/
David Brown <david.brown@hesbynett.no> writes:
> If you have 65535 people, and each of them eat 65535 jelly beans, they > have eaten 4294901761 jelly beans in total - they have not eaten -65535 > beans.
Um, 4294836225, but the point is well stated.
The 2026 Embedded Online Conference