Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #97397 > unrolled thread

Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable?

Started byFrancois Grieu <fgrieu@free.fr>
First post2016-11-24 15:33 +0100
Last post2016-11-24 07:40 -0800
Articles 7 on this page of 27 — 9 participants

Back to article view | Back to comp.lang.c


Contents

  Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Francois Grieu <fgrieu@free.fr> - 2016-11-24 15:33 +0100
    Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-11-24 14:59 +0000
      Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Francois Grieu <fgrieu@free.fr> - 2016-11-24 18:34 +0100
        Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? supercat@casperkitty.com - 2016-11-24 14:03 -0800
        Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? David Brown <david.brown@hesbynett.no> - 2016-11-25 09:50 +0100
      Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Noob <root@127.0.0.1> - 2016-11-25 09:44 +0100
        Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? David Brown <david.brown@hesbynett.no> - 2016-11-25 11:14 +0100
          Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-11-25 11:12 +0000
            Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? supercat@casperkitty.com - 2016-11-25 10:50 -0800
              Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Keith Thompson <kst-u@mib.org> - 2016-11-25 12:12 -0800
                Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Nick Bowler <nbowler@draconx.ca> - 2016-11-25 21:45 +0000
                Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? supercat@casperkitty.com - 2016-11-25 13:58 -0800
                  Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Keith Thompson <kst-u@mib.org> - 2016-11-28 23:37 -0800
                    Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-11-29 12:01 +0000
                    Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? supercat@casperkitty.com - 2016-11-29 07:39 -0800
                      Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Keith Thompson <kst-u@mib.org> - 2016-11-29 09:12 -0800
                Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-11-25 22:54 +0000
            Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Nick Bowler <nbowler@draconx.ca> - 2016-11-25 22:01 +0000
              Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-11-25 23:22 +0000
                Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Tim Rentsch <txr@alumni.caltech.edu> - 2016-12-10 07:22 -0800
      Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Tim Rentsch <txr@alumni.caltech.edu> - 2016-12-01 10:16 -0800
        Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Barry Schwarz <schwarzb@dqel.com> - 2016-12-01 11:06 -0800
          Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? supercat@casperkitty.com - 2016-12-01 12:40 -0800
          Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? David Brown <david.brown@hesbynett.no> - 2016-12-02 09:28 +0100
          Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? Tim Rentsch <txr@alumni.caltech.edu> - 2016-12-10 07:13 -0800
            Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? supercat@casperkitty.com - 2016-12-10 10:41 -0800
    Re: Is uint32_t foo(uint32_t x) { return (uint32_t)(x*x); } portable? supercat@casperkitty.com - 2016-11-24 07:40 -0800

Page 2 of 2 — ← Prev page 1 [2]


#97827

FromTim Rentsch <txr@alumni.caltech.edu>
Date2016-12-01 10:16 -0800
Message-ID<kfneg1ro6qn.fsf@x-alumni2.alumni.caltech.edu>
In reply to#97398
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:

> Francois Grieu <fgrieu@free.fr> writes:
>
>> Assume this code, under C99 or later
>>
>> #include <stdint.h>
>> uint32_t foo(uint32_t x) { return (uint32_t)(x*x); }
>
> The cast is pointless.  The returned value is always converted to the
> return type of the function.

I agree the cast here is superfluous, but the second sentence is
not quite right.  The value of the return expression is converted
to the return type of the function /except/ in the case where the
type of the return expression is the same as the return type of
the function.  For most types this makes no difference, but it
can make a difference for floating point types.

[toc] | [prev] | [next] | [standalone]


#97835

FromBarry Schwarz <schwarzb@dqel.com>
Date2016-12-01 11:06 -0800
Message-ID<o1t04c5jd13n35ao3b4k913pt41hprhvg9@4ax.com>
In reply to#97827
On Thu, 01 Dec 2016 10:16:00 -0800, Tim Rentsch
<txr@alumni.caltech.edu> wrote:

>Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>
>> Francois Grieu <fgrieu@free.fr> writes:
>>
>>> Assume this code, under C99 or later
>>>
>>> #include <stdint.h>
>>> uint32_t foo(uint32_t x) { return (uint32_t)(x*x); }
>>
>> The cast is pointless.  The returned value is always converted to the
>> return type of the function.
>
>I agree the cast here is superfluous, but the second sentence is
>not quite right.  The value of the return expression is converted
>to the return type of the function /except/ in the case where the
>type of the return expression is the same as the return type of
>the function.  For most types this makes no difference, but it
>can make a difference for floating point types.

Could you please elaborate.  If both the expression and return value
have type T, isn't any such conversion a no-op?  

-- 
Remove del for email

[toc] | [prev] | [next] | [standalone]


#97842

Fromsupercat@casperkitty.com
Date2016-12-01 12:40 -0800
Message-ID<bb79d821-8234-4bde-ba50-ffbe40c8923c@googlegroups.com>
In reply to#97835
On Thursday, December 1, 2016 at 1:06:14 PM UTC-6, Barry Schwarz wrote:
> On Thu, 01 Dec 2016 10:16:00 -0800, Tim Rentsch
> >I agree the cast here is superfluous, but the second sentence is
> >not quite right.  The value of the return expression is converted
> >to the return type of the function /except/ in the case where the
> >type of the return expression is the same as the return type of
> >the function.  For most types this makes no difference, but it
> >can make a difference for floating point types.
> 
> Could you please elaborate.  If both the expression and return value
> have type T, isn't any such conversion a no-op?  

There are some implementations that can't perform math on values of type
"float" directly, but instead convert them to "double" or "long double"
first.  On such an implementation, given the code:

    float sum1(float x, float y) { return x+y; }
    float sum2(float x, float y) { return (float)(x+y); }

    float a=16777216.0f;
    float b=1.0f;

    double d1a = a + b;;
    double d1b = (float)(a+b);
    double d2a = sum1(a,b);
    double d2b = sum2(a,b);

d1a would very likely receive 16777217.0 even though that value is not
representable as a float.  The cast before the assignment to d1b, however,
would force the value (a+b) to be rounded to the nearest value that is
actually representable as a "float" prior to the assignment.

The Standard presumes that code which uses "float" as a return type does so
to avoid forcing an implementation to expend extra effort returning a
"double", and not to prevent an implementation from returning a double in
cases where that would be faster than returning a float.  Thus, variable
d2a might receive 16777217.0 even though that is not representable as a
float, but d2b would receive 1677716.0 since that's the closest value that
would be representable as float.

[toc] | [prev] | [next] | [standalone]


#97862

FromDavid Brown <david.brown@hesbynett.no>
Date2016-12-02 09:28 +0100
Message-ID<o1rb9j$o6u$1@dont-email.me>
In reply to#97835
On 01/12/16 20:06, Barry Schwarz wrote:
> On Thu, 01 Dec 2016 10:16:00 -0800, Tim Rentsch
> <txr@alumni.caltech.edu> wrote:
> 
>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>
>>> Francois Grieu <fgrieu@free.fr> writes:
>>>
>>>> Assume this code, under C99 or later
>>>>
>>>> #include <stdint.h>
>>>> uint32_t foo(uint32_t x) { return (uint32_t)(x*x); }
>>>
>>> The cast is pointless.  The returned value is always converted to the
>>> return type of the function.
>>
>> I agree the cast here is superfluous, but the second sentence is
>> not quite right.  The value of the return expression is converted
>> to the return type of the function /except/ in the case where the
>> type of the return expression is the same as the return type of
>> the function.  For most types this makes no difference, but it
>> can make a difference for floating point types.
> 
> Could you please elaborate.  If both the expression and return value
> have type T, isn't any such conversion a no-op?  
> 

I interpret Tim's statement to mean that a no-op "conversion" is not a
conversion.

So for:

	int foo1(void) { return 1u; }

the unsigned "1u" is converted to int.

But for

	int foo2(void) { return 1; }

because "1" is already an int, there is no conversion.

So a more accurate version of Ben's statement would be "The returned
value is always converted to the return type of the function if it is
not already of that type".

(Corrections from Tim and Ben welcomed, of course.)

[toc] | [prev] | [next] | [standalone]


#98694

FromTim Rentsch <txr@alumni.caltech.edu>
Date2016-12-10 07:13 -0800
Message-ID<kfnd1gzlsv5.fsf@x-alumni2.alumni.caltech.edu>
In reply to#97835
Barry Schwarz <schwarzb@dqel.com> writes:

> On Thu, 01 Dec 2016 10:16:00 -0800, Tim Rentsch
> <txr@alumni.caltech.edu> wrote:
>
>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>
>>> Francois Grieu <fgrieu@free.fr> writes:
>>>
>>>> Assume this code, under C99 or later
>>>>
>>>> #include <stdint.h>
>>>> uint32_t foo(uint32_t x) { return (uint32_t)(x*x); }
>>>
>>> The cast is pointless.  The returned value is always converted to the
>>> return type of the function.
>>
>> I agree the cast here is superfluous, but the second sentence is
>> not quite right.  The value of the return expression is converted
>> to the return type of the function /except/ in the case where the
>> type of the return expression is the same as the return type of
>> the function.  For most types this makes no difference, but it
>> can make a difference for floating point types.
>
> Could you please elaborate.  If both the expression and return value
> have type T, isn't any such conversion a no-op?

It is fairly common for floating point operations to be carried
out in higher precision or range than the types of the operands
would imply.  The rule in C is that any higher precision/range
may persist until a result value is converted to a specific type.
Thus if we see, for example,

    double a, b, c;
    ...
    a = b+c;

the result of 'b+c' might have more precision than double does.
When the result is assigned to a (or cast to double) then any
extra precision/range must be discarded.  (Assignment operators
_always_ do a conversion, even if the types are the same.)  The
point of the rule for 'return' is that a statement like

    return  a*b+c;

need not discard any extra range/precision that may have come
about due to the operations performed (provided of course that
the type of the return expression matches the type of the
function).

Personally I think these rules are a bit screwy, but people
better informed about floating point than I am think they
make sense, so take that for what it's worth.

[toc] | [prev] | [next] | [standalone]


#98726

Fromsupercat@casperkitty.com
Date2016-12-10 10:41 -0800
Message-ID<12ef5bee-71a5-4697-9ee1-3755309faedc@googlegroups.com>
In reply to#98694
On Saturday, December 10, 2016 at 9:13:42 AM UTC-6, Tim Rentsch wrote:
> The point of the rule for 'return' is that a statement like
> 
>     return  a*b+c;
> 
> need not discard any extra range/precision that may have come
> about due to the operations performed (provided of course that
> the type of the return expression matches the type of the
> function).

The basic issue I think is that there are some systems where the cost
of returning a "long double" result would be greater than the cost of
returning a "float" or "double" result, but there are others which
use the same format for all floating-point return types.  It would be
silly to require that a programmer who wants a function to return a
value with as much precision as convenient would have to declare
functions as having different return types on different systems.

For some kinds of programming, allowing variables to behave as though they
hold extra precision may allow improved optimization, provided that there
is an easy way to force the shedding of such precision when required.  For
other types of programming where such shedding would be needed often, it
is more practical to have it performed implicitly.  Having directives to
control such things would seem to offer the best of both worlds.

Similar principles could also be applicable with integer math, but the
Standard doesn't allow any means by which programmers can grant or revoke
allowances for such optimizations.  For example, there's no integer type
which only needs to be capable of holding values -32768 to +32767 but
may arbitrarily and non-deterministically be capable of holding more, or
not, as the compiler sees fit.  If a 32-bit compiler defines int_fast16_t
so it fits in two octets, it must not allow any variables of that type to
hold values beyond the -32768 to +32767 range.  It can't say that such
a variable should be stored in memory as 16 bits, but may be kept in a
register with an unclipped 32-bit value.

[toc] | [prev] | [next] | [standalone]


#97402

Fromsupercat@casperkitty.com
Date2016-11-24 07:40 -0800
Message-ID<fa336acb-68e9-414f-97e4-7a2426d3dd70@googlegroups.com>
In reply to#97397
On Thursday, November 24, 2016 at 8:34:47 AM UTC-6, Francois Grieu wrote:
> In particular: when int is 64-bit, x gets promoted to int and becomes signed, 
> and x*x can overflow the range of int; e.g. for x=(uint32_t)(-1);
> So, what chapter and verse (if any) saves the day?

In earlier times, the Principle of Least Astonishment would have saved
the day.  Compiler writers would have recognized that forcing programmers
to write the expression as 1u*x*x is Just Plain Dumb.  As it is, however,
I know that gcc will sometimes violate the POLA when multiplying two
unsigned short values and storing the result as unsigned.

[toc] | [prev] | [standalone]


Page 2 of 2 — ← Prev page 1 [2]

Back to top | Article view | comp.lang.c


csiph-web