Getting intermediate results in 64 bits

R

Richard Cavell

Hi,

Using GCC on my G4, if I have a calculation like this:

#include <stdint.h>

uint64_t a = 0xffff * 0xffff ;

the result will be clobbered to 32 bits because that's the length of an
integer. How do I keep all the intermediate steps at 64 bits without
peppering my operands with (uint64_t) casts?

It would help if my default sizeof(int) were 8, I guess. How can I
obtain this?
 
W

Walter Roberson

Using GCC on my G4, if I have a calculation like this:
#include <stdint.h>
uint64_t a = 0xffff * 0xffff ;

Integral values explicitly specified default to 'int' unless there
is a type modifier or the context provides reason to use a wider
type.

the result will be clobbered to 32 bits because that's the length of an
integer. How do I keep all the intermediate steps at 64 bits without
peppering my operands with (uint64_t) casts?

In terms of code changes, you could use:

uint64_t a = 0xffff * (unit64_t) 0xffff ;

or you could use

uint64_t a = 0xffffLU * 0xffffLU;

It would help if my default sizeof(int) were 8, I guess. How can I
obtain this?

That is implimentation specific. The version of gcc that I have handy
does not list options for the G4; I suggest you check your gcc
man page under the section "Configuration Dependent Options";
and if you do not find it there then try looking at the .info files
that a lot of the real documentation is in for FSF products.

As an example, on MIPS systems (e.g., SGI), there is -mint64
 
B

Ben Pfaff

Richard Cavell said:
Using GCC on my G4, if I have a calculation like this:

#include <stdint.h>

uint64_t a = 0xffff * 0xffff ;

the result will be clobbered to 32 bits because that's the length of
an integer. How do I keep all the intermediate steps at 64 bits
without peppering my operands with (uint64_t) casts?

Designate one operand as type `unsigned long long':
uint64_t a = 0xffffULL * 0xffff;
 
K

Keith Thompson

Integral values explicitly specified default to 'int' unless there
is a type modifier or the context provides reason to use a wider
type.

The type of an expression is not affected by the context in which it
appears. (A null pointer constant in a pointer context is the only
exception I can think of.) If it were, 0xffff and the result of the
multiplication would inherit the uint64_t type provided by the
context.

Very often the result of an expression is implicitly converted to some
type imposed by its context. For example (assuming 32-bit int),
given:

uint64_t a = 0xffff;

the constant 0xffff is of type int, but the result is implicitly
converted to uint64_t before being used to initialize a. But these
context-driven implicit conversions happen only on a single level;
the uint64_t context in

uint64_t a = 0xffff * 0xffff;

affects only the result of the multiplication, not the operands.

This makes using the fixed-width types in <stdint.h> tricky. You can
declare everything to be of some fixed size, and never refer to the
predefined types short, int, long, et al, but the ranges of the
predefined types can still affect the semantics of your code. The
language provides suffixes for signed and unsigned long and long long,
but not for the fixed-width types. Casts are probably the best
approach (one of the rare cases where casts are useful in portable
code).
 
C

Chris Torek

The type of an expression is not affected by the context in which it
appears. (A null pointer constant in a pointer context is the only
exception I can think of.)

I think this is not such a good way to put it. It might be better
to say something like "contexts are extremely local".
If it were, 0xffff and the result of the multiplication would
inherit the uint64_t type provided by the [result] context
[since this was "uint64_t result = expr1 * expr2"].
Very often the result of an expression is implicitly converted to some
type imposed by its context. For example (assuming 32-bit int),
given:

uint64_t a = 0xffff;

the constant 0xffff is of type int, but the result is implicitly
converted to uint64_t before being used to initialize a. But these
context-driven implicit conversions happen only on a single level;
the uint64_t context in

uint64_t a = 0xffff * 0xffff;

affects only the result of the multiplication, not the operands.

Just so: the context is as localized as compiler-ly possible, which
may often be "more local" than the programmer intends.
This makes using the fixed-width types in <stdint.h> tricky. You can
declare everything to be of some fixed size, and never refer to the
predefined types short, int, long, et al, but the ranges of the
predefined types can still affect the semantics of your code. ...

Indeed. Worse, the "value preserving" rules make the effects of
widening unpredictable in "questionably signed" cases, so that:

uint16_t a, b, c;
...
if (a >= (b - c))

gives different results on 16- and 32-bit machines: the former uses
an unsigned compare, and the latter a signed compare.
 
W

websnarf

Richard said:
Using GCC on my G4, if I have a calculation like this:

Technically GCC and G4 are off topic here. But whatever.
#include <stdint.h>

Wow, lucky you -- you happen to have one of these lying around.
uint64_t a = 0xffff * 0xffff ;

the result will be clobbered to 32 bits because that's the length
of an integer. How do I keep all the intermediate steps at 64 bits
without peppering my operands with (uint64_t) casts?

Actually take a look into stdint.h more closely and you will see:

uint64_t a = UINT64_C(0xffff) * UINT64_C(0xffff);

That's the official way anyhow. Not much better than casting, but it
works. Of course if we were taking into account that you are using
gcc, then the answer is a little more simple:

unsigned long long a = 0xffffull * 0xffffull;

But then, this is not portable.
 
K

Keith Thompson

Chris Torek said:
I think this is not such a good way to put it. It might be better
to say something like "contexts are extremely local".

Hmm. I still like the way I phrased it.

Given:

short a = 10;
int b = 20;
long c = 30;

all three integer constants are of type int. The constant 10 is then
implicitly converted to type short, and 30 is implicitly converted to
type long, but I think of the implicit conversions as separate
operations, not impositions of the context type on the expression.
 
O

Old Wolf

Keith said:
context is the only exception I can think of.)

Hmm. I still like the way I phrased it.
Given:

short a = 10;
int b = 20;
long c = 30;

all three integer constants are of type int.

What did you have in mind with the "A null pointer constant..."
exception? In

void *p = 0;
or
memset(0, 0, 0);

the 0s are all ints (which then undergo conversion to pointers).
 
K

Keith Thompson

Old Wolf said:
What did you have in mind with the "A null pointer constant..."
exception? In

void *p = 0;
or
memset(0, 0, 0);

the 0s are all ints (which then undergo conversion to pointers).

What I had in mind was not entirely coherent. Null pointer constants
are not an exception.
 
T

Tim Rentsch

Keith Thompson said:
Old Wolf said:
[snip]
The type of an expression is not affected by the context in
which it appears. (A null pointer constant in a pointer
context is the only exception I can think of.)
[snip]

What did you have in mind with the "A null pointer constant..."
exception? [...]

What I had in mind was not entirely coherent. Null pointer constants
are not an exception.

So what you meant to say was, A null pointer constant in a pointer
context is the only exception you can think of, and even that's not an
exception? :)

On a related topic - would anyone like to opine as to whether

int (*pf)(int);
pf = (void*)(int*)(void*) 0;

is permitted by the standard or not? How about

pf = (void*)(int*) 0;

or

pf = (void*)(void*) 0;

The statements in 6.3.2.3 p3 and p4 seem to imply that the right hand
side expressions are not null pointer constants.
 
K

Keith Thompson

Tim Rentsch said:
Keith Thompson said:
What did you have in mind with the "A null pointer constant..."
exception? [...]

What I had in mind was not entirely coherent. Null pointer constants
are not an exception.

So what you meant to say was, A null pointer constant in a pointer
context is the only exception you can think of, and even that's not an
exception? :)

Exactly, except that I didn't think of the "and even that's not an
exception" part until later.
 
R

Richard Cavell

Actually take a look into stdint.h more closely and you will see:

uint64_t a = UINT64_C(0xffff) * UINT64_C(0xffff);

Does it distribute across any arbitrary number of operands?

uint64_t a = UINT64_C ( b + c - d * e % f << 3 ) ;
 
C

Christian Kandeler

Richard said:
Does it distribute across any arbitrary number of operands?

uint64_t a = UINT64_C ( b + c - d * e % f << 3 ) ;

It's only for constants.


Christian
 
C

Christian Kandeler

Richard said:
Does it distribute across any arbitrary number of operands?

uint64_t a = UINT64_C ( b + c - d * e % f << 3 ) ;

It's only for constants.


Christian
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,763
Messages
2,569,563
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top