C++ Logo

std-proposals

Advanced search

Re: Mistake in C++ Standard Arithmetic Conversion makes C++ compiler unreliable

From: John McFarlane <john_at_[hidden]>
Date: Tue, 7 Jan 2020 21:19:48 +0000
On Tue, 7 Jan 2020 at 06:46, Владимир Прокофьев via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> I have found a mistake in the C++ Standard,
> https://en.cppreference.com/w/cpp/language/operator_arithmetic#Conversion :
> in the rule:
>
> *Otherwise, if the unsigned operand's conversion rank is greater or equal
> to the conversion rank of the signed operand, the signed operand is
> converted to the unsigned operand's type.*
>
> The NEGATIVE signed value CANNOT be converted to unsigned for any types.
> But opposite conversion from unsigned to signed is valid with appropriate
> rank.
>
Surely this depends on the value of the operands. For example, the
expression "0x80000000U/2" comes out just fine. Would that still be the
case if signed -- not unsigned -- was chosen on the rank tie-breaker?

> To prove, please see wrong results here https://rextester.com/CKKDX46498
>
(Minor point: do your row and column headings have the wrong signedness?)

> Note, that the error is observed for VS, CLang and GCC C++ compilers.
>
> As you can see the error is observer in *division* operation to *unsigned*
> *32* and *64* bits.
> The *division* operation to *SIGNED* types is VALID (see *first* table in
> the example).
> Also *multiplication* of signed and unsigned values is VALID.
>
You mean multiplication is mathematically correct *with* the 'fix', right?
I think you can make the same claim with most of the binary operators.
Division is perhaps among the more surprising operators in this respect but
I believe this design choice is fairly consistent. For example "-1<1U"
evaluates to false.

> The example above shows that the C++ compiler shows *unreliable* behavior
> which leads to produce wrong values as a result of division.
>
> The correct rule *shall* be:
>
> *Otherwise, unsigned operand is converted to signed type of same or higher
> conversion rank of unsigned operand's type and then the operand with lesser
> conversion rank is converted to the operand with the greater conversion
> rank*
>
I agree that the choice that was made was unfortunate in the post-16-bit
era. But remember that when `int` was 16-bit, the alternative would have
meant that "40000<30000" was true. Also pretty surprising.

Ultimately, there's no way you can guarantee the correct result with
mixed-signedness operands. So the rule we really need to enforce (in
tooling) is "ES.100: Don't mix signed and unsigned arithmetic
<https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es100-dont-mix-signed-and-unsigned-arithmetic>
".

John

> С уважением.
>
> Владимир.
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2020-01-07 15:22:31