ptrdiff_t maximum

J

John Kelly

I changed this to make the output easier to read. If some 64-bit folks
could run it and post the output, I would appreciate it.




# include <stdio.h>
# include <stddef.h>

int
main (void)
{
ptrdiff_t last, next;
printf ("\n");
printf (" last");
printf (" next");
printf (" double");
printf ("\n\n");
for (last = 32767; (next = 2 * (double) last + 1) > last;) {
printf ("%22lld %22lld %32f\n", (long long) last, (long long) next,
2 * (double) last + 1);
last = next;
}
printf ("\n%22lld\n%22llx\n\n", (long long) last, (long long) last);
return 0;
}
 
G

Geoff

I changed this to make the output easier to read. If some 64-bit folks
could run it and post the output, I would appreciate it.




# include <stdio.h>
# include <stddef.h>

int
main (void)
{
ptrdiff_t last, next;
printf ("\n");
printf (" last");
printf (" next");
printf (" double");
printf ("\n\n");
for (last = 32767; (next = 2 * (double) last + 1) > last;) {
printf ("%22lld %22lld %32f\n", (long long) last, (long long) next,
2 * (double) last + 1);
last = next;
}
printf ("\n%22lld\n%22llx\n\n", (long long) last, (long long) last);
return 0;
}

I don't know why you're fooling around with this but here you go.
It seems to me that INT_MAX/INT_MIN or UINT_MAX would serve the purpose
if you don't have SIZE_MAX.

last next double

32767 65535 65535.000000
65535 131071 131071.000000
131071 262143 262143.000000
262143 524287 524287.000000
524287 1048575 1048575.000000
1048575 2097151 2097151.000000
2097151 4194303 4194303.000000
4194303 8388607 8388607.000000
8388607 16777215 16777215.000000
16777215 33554431 33554431.000000
33554431 67108863 67108863.000000
67108863 134217727 134217727.000000
134217727 268435455 268435455.000000
268435455 536870911 536870911.000000
536870911 1073741823 1073741823.000000
1073741823 2147483647 2147483647.000000
2147483647 4294967295 4294967295.000000
4294967295 8589934591 8589934591.000000
8589934591 17179869183 17179869183.000000
17179869183 34359738367 34359738367.000000
34359738367 68719476735 68719476735.000000
68719476735 137438953471 137438953471.000000
137438953471 274877906943 274877906943.000000
274877906943 549755813887 549755813887.000000
549755813887 1099511627775 1099511627775.000000
1099511627775 2199023255551 2199023255551.000000
2199023255551 4398046511103 4398046511103.000000
4398046511103 8796093022207 8796093022207.000000
8796093022207 17592186044415 17592186044415.000000
17592186044415 35184372088831 35184372088831.000000
35184372088831 70368744177663 70368744177663.000000
70368744177663 140737488355327 140737488355327.000000
140737488355327 281474976710655 281474976710655.000000
281474976710655 562949953421311 562949953421311.000000
562949953421311 1125899906842623 1125899906842623.000000
1125899906842623 2251799813685247 2251799813685247.000000
2251799813685247 4503599627370495 4503599627370495.000000
4503599627370495 9007199254740991 9007199254740991.000000
9007199254740991 18014398509481984 18014398509481984.000000
18014398509481984 36028797018963968 36028797018963968.000000
36028797018963968 72057594037927936 72057594037927936.000000
72057594037927936 144115188075855872 144115188075855870.000000
144115188075855872 288230376151711744 288230376151711740.000000
288230376151711744 576460752303423488 576460752303423490.000000
576460752303423488 1152921504606846976 1152921504606847000.000000
1152921504606846976 2305843009213693952 2305843009213694000.000000
2305843009213693952 4611686018427387904 4611686018427387900.000000

4611686018427387904
4000000000000000
 
J

John Kelly

I don't know why you're fooling around with this but here you go.
It seems to me that INT_MAX/INT_MIN or UINT_MAX would serve the purpose
if you don't have SIZE_MAX.

I have a macro for SIZE_MAX, but I also need PTRDIFF_MAX. I compare
them and use the lesser of the two. Old C has no PTRDIFF_MAX macro so I
have to derive it as best I can.

last next double

32767 65535 65535.000000
65535 131071 131071.000000
131071 262143 262143.000000
262143 524287 524287.000000
524287 1048575 1048575.000000
1048575 2097151 2097151.000000
2097151 4194303 4194303.000000
4194303 8388607 8388607.000000
8388607 16777215 16777215.000000
16777215 33554431 33554431.000000
33554431 67108863 67108863.000000
67108863 134217727 134217727.000000
134217727 268435455 268435455.000000
268435455 536870911 536870911.000000
536870911 1073741823 1073741823.000000
1073741823 2147483647 2147483647.000000
2147483647 4294967295 4294967295.000000
4294967295 8589934591 8589934591.000000
8589934591 17179869183 17179869183.000000
17179869183 34359738367 34359738367.000000
34359738367 68719476735 68719476735.000000
68719476735 137438953471 137438953471.000000
137438953471 274877906943 274877906943.000000
274877906943 549755813887 549755813887.000000
549755813887 1099511627775 1099511627775.000000
1099511627775 2199023255551 2199023255551.000000
2199023255551 4398046511103 4398046511103.000000
4398046511103 8796093022207 8796093022207.000000
8796093022207 17592186044415 17592186044415.000000
17592186044415 35184372088831 35184372088831.000000
35184372088831 70368744177663 70368744177663.000000
70368744177663 140737488355327 140737488355327.000000
140737488355327 281474976710655 281474976710655.000000
281474976710655 562949953421311 562949953421311.000000
562949953421311 1125899906842623 1125899906842623.000000
1125899906842623 2251799813685247 2251799813685247.000000
2251799813685247 4503599627370495 4503599627370495.000000
4503599627370495 9007199254740991 9007199254740991.000000
9007199254740991 18014398509481984 18014398509481984.000000
18014398509481984 36028797018963968 36028797018963968.000000
36028797018963968 72057594037927936 72057594037927936.000000
72057594037927936 144115188075855872 144115188075855870.000000
144115188075855872 288230376151711744 288230376151711740.000000
288230376151711744 576460752303423488 576460752303423490.000000
576460752303423488 1152921504606846976 1152921504606847000.000000
1152921504606846976 2305843009213693952 2305843009213694000.000000
2305843009213693952 4611686018427387904 4611686018427387900.000000

4611686018427387904
4000000000000000


It's stops one step short of what I expected. I suppose that's due to
precision loss when converting back to integer. That's OK, strings of
those sizes would be ridiculous anyway. As long as I don't go over, it
works.

Thanks Geoff.
 
G

Geoff

It's stops one step short of what I expected. I suppose that's due to
precision loss when converting back to integer. That's OK, strings of
those sizes would be ridiculous anyway. As long as I don't go over, it
works.

Yes, I found that strange also. Francesco's run ended at the right step on his
AMD once he got a 64-bit binary. I don't have time right now to investigate
further.
 
F

Francesco S. Carta

I didn't get any warnings. Your compiler appears to not need the %lld,
use %ld instead.

Seems so, yes. Changing it I got rid of all warnings, thank you.
 
B

Ben Bacarisse

Ian Collins said:
In C99, all you need is

printf("ptrdiff_t maximum %ld\n", PTRDIFF_MAX);

:)

Yes, of course. I mean to use the same technique to find a signed
type's maximum, you need intmax_t.
Dreaded or mythical?

I think the best current description is historical (at least as far as
integer types are concerned). The trouble is that history repeats
itself.
 
B

Ben Bacarisse

John Kelly said:
Seems to work.

It relies on the implementation defined behaviour of the out of range
conversion of a double to a ptrdiff_t value.

<snip>
 
K

Keith Thompson

Ben Bacarisse said:
It relies on the implementation defined behaviour of the out of range
conversion of a double to a ptrdiff_t value.

<snip>

It's undefined, not implementation-defined. C99 6.3.1.4p1.
 
J

John Kelly

It relies on the implementation defined behaviour of the out of range
conversion of a double to a ptrdiff_t value.

I can't think of a standards-proof way of doing it, and ISTM the vast
majority of implementations would handle it sanely and silently.

If there's a better way, I'm open to suggestions.

On new platforms with PTRDIFF_MAX defined, the issue is moot. I could
manually #ifdef old platforms with their correct value for PTRDIFF_MAX,
but that work is not much fun. So I tried to accommodate them using an
automated solution.

Anyone running the code in a pre-C99 environment, can use the algorithm
to determine the correct value of PTRDIFF_MAX, and #define the constant
they need.
 
P

Peter Nilsson

Ben Bacarisse said:
That swaps overflow for an out-of-range conversion which is
implementation defined and can raise a signal (in C99).

Hence the 'Under C90' prefice. But the implementation defined
out-of-range conversion must yield a value. Since that value
must be in the range of ptrdiff_t, and since it's maximum
is of the form 2^N-1, the loop can only stop when the maximum
positive value is reached.
 
I

Ian Collins

Seems to work.

If it loses precision approaching the 64th bit, that's acceptable for
purposes of trim(). I only need to stay under the limit, without going
over.

The solution I proposed doesn't hit any limits and yields a compile time
constant. Yes it may run foul of padding bits, but I don't know of any
system made today that has those.
 
J

John Kelly

Hence the 'Under C90' prefice. But the implementation defined
out-of-range conversion must yield a value. Since that value
must be in the range of ptrdiff_t, and since it's maximum
is of the form 2^N-1, the loop can only stop when the maximum
positive value is reached.

An out-of-range conversion that never produces a value less than or
equal the previous would be problematic. But eventually you run out of
numbers at the high end, so that's impossible.
 
J

John Kelly

The solution I proposed doesn't hit any limits and yields a compile time
constant. Yes it may run foul of padding bits, but I don't know of any
system made today that has those.

A compile time constant is preferable. But the dynamic solution is
unbreakable, and it's only needed for pre-C99 environments. If people
really want to use this code, and go to the trouble of determining the
right constant for their environment, I can add an #ifdef for it.

But that work is not fun, and I'm not doing it all by myself.
 
B

Ben Bacarisse

Keith Thompson said:
It's undefined, not implementation-defined. C99 6.3.1.4p1.

So it is. Thanks for pointing it out. Presumably this is because there
has been hardware for which the implementation-defined options available
for out of range integer conversions are not flexible enough.
 
B

Ben Bacarisse

Peter Nilsson said:
Hence the 'Under C90' prefice. But the implementation defined
out-of-range conversion must yield a value. Since that value
must be in the range of ptrdiff_t, and since it's maximum
is of the form 2^N-1, the loop can only stop when the maximum
positive value is reached.

That's pleasantly sneaky!
 
J

John Kelly

That's pleasantly sneaky!

No.

The loop will stop when the next value is not greater than the current
one. It has an upper bound, but you may terminate before reaching it.
 
U

Uno

John said:
It's stops one step short of what I expected. I suppose that's due to
precision loss when converting back to integer. That's OK, strings of
those sizes would be ridiculous anyway. As long as I don't go over, it
works.

Thanks Geoff.
$ gcc -std=c99 -Wall -Wextra jk2.c -o out
$ ./out

last next double

32767 65535 65535.000000
65535 131071 131071.000000
131071 262143 262143.000000
262143 524287 524287.000000
524287 1048575 1048575.000000
1048575 2097151 2097151.000000
2097151 4194303 4194303.000000
4194303 8388607 8388607.000000
8388607 16777215 16777215.000000
16777215 33554431 33554431.000000
33554431 67108863 67108863.000000
67108863 134217727 134217727.000000
134217727 268435455 268435455.000000
268435455 536870911 536870911.000000
536870911 1073741823 1073741823.000000
1073741823 2147483647 2147483647.000000

2147483647
7fffffff

$ gcc -std=c99 -Wall -Wextra jk1.c -o out
$ ./out
ptrdiff_t max1 is 2147483647
ptrdiff_t max2 is 2147483647

$ uname -a
Linux dan-desktop 2.6.28-17-generic #58-Ubuntu SMP Tue Dec 1 18:57:07
UTC 2009 i686 GNU/Linux
$

All looks good to me.

John, I came in late to this discussion and can't crack what's going on
in your first version. I get lost fairly quickly on this:
ptrdiff_t const dx =
((ptrdiff_t) ~ ((ptrdiff_t) - 1 < 1 ? (-1 - (((ptrdiff_t) 1
<< (sizeof (ptrdiff_t) * 8 - 2)) - 1 + ((ptrdiff_t) 1 << (sizeof
(ptrdiff_t) * 8 - 2)))) : (ptrdiff_t) 0));

Is there a way to break this statement up into smaller, more
understandable chunks. To my eye, ptrdiff_t is a type that gets treated
like a variable. The bitwise negation of an expression with the query
syntax is way beyond my experience, to boot

I'm not too popular around here, with the topic worshipers in
particular, but that's not everyone.
 
J

John Kelly

John, I came in late to this discussion and can't crack what's going on
in your first version. I get lost fairly quickly on this:
ptrdiff_t const dx =
((ptrdiff_t) ~ ((ptrdiff_t) - 1 < 1 ? (-1 - (((ptrdiff_t) 1
<< (sizeof (ptrdiff_t) * 8 - 2)) - 1 + ((ptrdiff_t) 1 << (sizeof
(ptrdiff_t) * 8 - 2)))) : (ptrdiff_t) 0));

Is there a way to break this statement up into smaller, more
understandable chunks. To my eye, ptrdiff_t is a type that gets treated
like a variable. The bitwise negation of an expression with the query
syntax is way beyond my experience, to boot

It looks like an obfuscation contest entry doesn't it. I abandoned it
for a better solution, which I posted in v3 of trim().

I'm not too popular around here, with the topic worshipers in
particular, but that's not everyone.

I don't expect to win a popularity contest either. But I'm not in high
school anymore and my priorities have matured.
 
P

Peter Nilsson

John Kelly said:

It may not be pleasing or sneaky, but it does work under C90.
[Aside: Lawrence Kirby posted the first version I saw.]
The loop will stop when the next value is not greater than
the current one.
Correct.

 It has an upper bound,
Correct.

but you may terminate before reaching it.

No. If max (which is of the form 2^N-1) is not the upper
bound, then 2^(N+1)-1 is representable and the loop will
continue as conversions are value preserving if the value
is in range. Thus, 'new' obtains a value that is not
greater than the previous one if, and only if, max is at
the upper bound limit.
 
J

John Kelly

John Kelly said:

It may not be pleasing or sneaky, but it does work under C90.
[Aside: Lawrence Kirby posted the first version I saw.]
The loop will stop when the next value is not greater than
the current one.
Correct.

 It has an upper bound,
Correct.

but you may terminate before reaching it.

No. If max (which is of the form 2^N-1) is not the upper
bound, then 2^(N+1)-1 is representable and the loop will
continue as conversions are value preserving if the value
is in range.

OK. But I'm scared of the UB boogeyman. Are you sure they are value
preserving if the value is in range?
 

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

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,128
Latest member
ElwoodPhil
Top