Subject: Re: Documentation of abs(3), div(3) etc.
To: Martijn van Buul <pino+gmane_os_netbsd_devel_general@dohd.org>
From: Krister Walfridsson <cato@df.lth.se>
List: tech-misc
Date: 02/10/2007 15:47:42
On Fri, 9 Feb 2007, Martijn van Buul wrote:

> My point was that the proposed change indicates is pointless, doesn't
> change a single opcode, and obviously indicates a lack of understanding.

It potentially does, depending on the surrounding code and the compiler
you are using.


> If you really think that
>
> signed int a;
> return (signed int) ( - (unsigned)a );
>
> is in any better than
>
> signed int a;
> return -a;
>
> then I kindly suggest you catch up with how C works.

It is.  Integer types must not wrap in C

   ISO/IEC 9899:1999 6.5p5:
   If an exceptional condition occurs during the evaluation of an
   expression (that is, if the result is not mathematically defined
   or not in the range of representable values for its type), the
   behavior is undefined.

so

   signed int a;
   return -a;

gives you undefined behavior if a = INT_MIN.  The compiler may therefore
do anything (the canonical example is erase your hard disk, but the more
realistic is doing optimizations based on assuming a != INT_MIN).


For

   signed int a;
   return (signed int) ( - (unsigned)a );

on the other hand, you do the operation on an unsigned type, which
is permitted by the standard.

   ISO/IEC 9899:1999 6.2.5p9:
   [...] A computation involving unsigned operands can never overflow,
   because a result that cannot be represented by the resulting unsigned
   integer type is reduced modulo the number that is one greater than the
   largest value that can be represented by the resulting type.

You also do conversions between possibly unrepresentable values.
This is completely defined in one direction

   ISO/IEC 9899:1999 6.3.1.3p2:
   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.

and it is implementation-defined in the other direction

   ISO/IEC 9899:1999 6.3.1.3p3:
   Otherwise, the new type is signed and the value cannot be represented
   in it; either the result is implementation-defined or an implementation-
   defined signal is raised.

i.e. you need to check the documentation for your compiler, but I think
that all compilers on the market do what you would naively expect.


And this is not abstract language layering pedantry -- it does affect
real code as compilers get better at doing value range analysis, whole
program optimizations, etc., and there have recently been looooong
discussions on the gcc lists where people have been surprised that gcc
has eliminated their "wrap-around" tests of the type "if (a+100 < a)"
(which would not have happened if it was done using unsigned arithmetics).
See e.g. the gcc bug report

   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30475

and the different discussions at

   http://gcc.gnu.org/ml/gcc/2006-12/

(search for "wrap" and "overflow" in the subject).

     /Krister