What are the minimum and maximum float numbers and integers?


Ad

Advertisements

B

Brad Baxter

Is there a way to get the minimum and maximum float numbers and
integers?

use warnings;
use strict;

sub min { $_[$_[0]>$_[1]] }
sub max { $_[$_[0]<$_[1]] }

my @a = ( 1, 2, 3 );

my( $max, $min ) = ($a[0])x2;

for( 1 .. $#a ) {
$max = max( $max, $a[$_] );
$min = min( $min, $a[$_] );
}

print "Max is: $max\n";
print "Min is: $min\n";


But you probably meant something else. :)
 
S

sln

Is there a way to get the minimum and maximum float numbers and
integers?

Something like this should get you in the range.

-sln

---------------
use strict;
use warnings;

my $exp = 10;
while (sprintf("%e", 10**$exp) !~ /inf/i) {
++$exp;
}
--$exp;

my $places = 6;
my $mant = 2 - .1; # Because 2 always overflows

while (sprintf("%e", $mant * 10**$exp) =~ /inf/i) {
$mant /= 1 + 10**(-$places);
}

printf ("UINT: %u\n", -1);
printf ("INT: +/- %d\n", sprintf ("%u", -1) / 2);
printf ("FLOAT: +/- %.".$places."e\n", $mant * 10**$exp);

__END__

UINT: 4294967295
INT: +/- 2147483647
FLOAT: +/- 1.797693e+308
 
I

ilovelinux

Something like this should get you in the range. [...]
printf ("INT:    +/- %d\n", sprintf ("%u", -1) / 2);

This didn't work for me; it printed:
INT: +/- -9223372036854775808
(platform = CYGWIN_NT-5.1 1.7.1(0.218/5/3))

Replacing it with:
printf ("INT: +/- %d\n", sprintf ("%u", -1) / 2 - 1);
didn't help, surprisingly:
INT: +/- -9223372036854775808

But the following did the trick:
printf ("INT: +/- %d\n", sprintf("%u", -1)>>1);
INT: +/- 9223372036854775807


As an aside, the format specification with the embedded $places in the
format, in
printf ("FLOAT:  +/- %.".$places."e\n", $mant * 10**$exp);
has a more official counterpart, which was derived from printf(3) and
also works in C:
printf ("FLOAT: +/- %.*e\n", $places, $mant * 10**$exp);

Instead of interpolating "$places", use a * in the format
specification and add $places as an extra argument to printf.
 
Ad

Advertisements

S

sln

Something like this should get you in the range. [...]
printf ("INT:    +/- %d\n", sprintf ("%u", -1) / 2);

This didn't work for me; it printed:
INT: +/- -9223372036854775808
(platform = CYGWIN_NT-5.1 1.7.1(0.218/5/3))
Scary stuff.
Is this run under Windows XP-64 or 32?
Is 64 bit native in the OS? What perl distribution?
Replacing it with:
printf ("INT: +/- %d\n", sprintf ("%u", -1) / 2 - 1);
didn't help, surprisingly:
INT: +/- -9223372036854775808
^
Since this is supposed to be 'unsigned', its not too suprising.
But the following did the trick:
printf ("INT: +/- %d\n", sprintf("%u", -1)>>1);
INT: +/- 9223372036854775807
I don't know why division by 2 should be any different than >>1.
Funny things can happen like the sign bit is duplicated during arithmatic.
But thats seems more a consequence for signed types less than the native
machine word (int) of a typed language like C.

Well, if shift right 1 (not division by 2) works for you, then you don't
need the sprintf(), a simple printf ("INT: +/- %d\n", -1>>1 );
will do.

What does
printf "%d", 18446744073709551615;
printf "%d", 18446744073709551615 / 2;
return? It should return:
-1
9223372036854775807
If not, arithmatic operations would be suspect.
Instead of interpolating "$places", use a * in the format
specification and add $places as an extra argument to printf.
^^
Thanks.

-sln
 
S

sln

On 26 jan, 18:52, (e-mail address removed) wrote:
[snip]
What does
printf "%d", 18446744073709551615;
printf "%d", 18446744073709551615 / 2;
return? It should return:
-1
9223372036854775807
If not, arithmatic operations would be suspect.

Or, to clarify (at least in your case), arithmatic operations
resulting in or on values outside the range of (+/-) signed int (machine word).
But Windows-64 will run 32-bit programs with sign emulation. It seems more a
processor feature though.

-sln
 
P

Peter J. Holzer

Something like this should get you in the range. [...]
printf ("INT:    +/- %d\n", sprintf ("%u", -1) / 2);

This didn't work for me; it printed:
INT: +/- -9223372036854775808
(platform = CYGWIN_NT-5.1 1.7.1(0.218/5/3))
Scary stuff.
Is this run under Windows XP-64 or 32?
Is 64 bit native in the OS? What perl distribution?
Replacing it with:
printf ("INT: +/- %d\n", sprintf ("%u", -1) / 2 - 1);
didn't help, surprisingly:
INT: +/- -9223372036854775808
^
Since this is supposed to be 'unsigned', its not too suprising.
But the following did the trick:
printf ("INT: +/- %d\n", sprintf("%u", -1)>>1);
INT: +/- 9223372036854775807
I don't know why division by 2 should be any different than >>1.

Because division is always done in floating point arithmetic in Perl.
So the result of (18446744073709551615/2) should be
9223372036854775807.5 - but that isn't representable in a 53 bit
mantissa, so it is rounded to the next representable value which happens
to be 9223372036854775808 (the next lower representable FP value btw is
9223372036854774784, so subtracting any value <= 512 doesn't have any
effect).

If int(18446744073709551615/2) gives you 9223372036854775807 you
probably have compiled perl to use long double arithmetic.
Funny things can happen like the sign bit is duplicated during arithmatic.

C doesn't define[1] what happens if you right-shift a negative value.

But Perl does, at least if don't "use integer":

| Note that both "<<" and ">>" in Perl are implemented directly using
| "<<" and ">>" in C. If "use integer" (see "Integer Arithmetic") is in
| force then signed C integers are used, else unsigned C integers are
| used.
(perldoc perlop)

So (-1 >> 1) uses unsigned integer operations. (unsigned)-1 is
guaranteed to be the largest unsigned integer, so (-1 >> 1) is half of
that which is half the largest signed integer (unless there are unused
bits in the implementation, which C allows, but that's exceedingly
rare).

hp

[1] Although I think it's implementation-defined, not undefined.
 
D

Dr.Ruud

Peter said:
So (-1 >> 1) uses unsigned integer operations. (unsigned)-1 is
guaranteed to be the largest unsigned integer, so (-1 >> 1) is half of
that which is half the largest signed integer (unless there are unused
bits in the implementation, which C allows, but that's exceedingly
rare).

ITYM: "(-1) >> 1" is the largest signed integer: 0/1/.
(a 0-bit, and then as many 1-bits as possible)
 
P

Peter J. Holzer

ITYM: "(-1) >> 1" is the largest signed integer:

Unary - binds closer than binary >>, so (-1 >> 1) is the same as
((-1) >> 1). So I don't see how that differs from what I wrote.

0/1/. (a 0-bit, and then as many 1-bits as possible)

Yes.

hp
 
Ad

Advertisements

P

Peter J. Holzer

Unary - binds closer than binary >>, so (-1 >> 1) is the same as
((-1) >> 1). So I don't see how that differs from what I wrote.

Ah, I see it now: "half the largest signed integer" should of course
have read "the largest signed integer". Somehow I duplicated the "half".

hp
 
S

sln

Because division is always done in floating point arithmetic in Perl.

Doh, what was I thinking. Of course.

So the result of (18446744073709551615/2) should be
9223372036854775807.5 - but that isn't representable in a 53 bit
mantissa, so it is rounded to the next representable value which happens
to be 9223372036854775808 (the next lower representable FP value btw is
9223372036854774784, so subtracting any value <= 512 doesn't have any
effect).

In C, the value passed to printf using formatter %d must be an integer (signed or not).
So the result of the division, a float, must be coerced to integer:
printf ("%d", (int)(18446744073709551615/2) );
Does Perl do this coersion in C or does it do its own rounding then chopping via
custom methods?

I guess the IEEE 87 standard is 53 bit mantissa so it can't handle a 64 bit
(-1) u_integer division, but would handle 32 bit u_integers. That explains
how printf ("%d", 4294967295/2); works, but how can the previous poster's
64 bit result be -9223372036854775808 which is almost correct?
If his Perl is using long-double shouldn't this extend the mantissa past 53 bits?

As an aside, I wonder if Perl is sometimes compiled to take advantage of
faster SSE-2 64-bit in lieu of precision 80 bit. I know when I compile C++
I don't enable support for those instructions, and usually select precision
over speed.

Also, I thought cpu's do integer arithmetic +-/*

Thanks.

-sln
 
P

Philip Potter

C doesn't define[1] what happens if you right-shift a negative value.

But Perl does, at least if don't "use integer":

| Note that both "<<" and ">>" in Perl are implemented directly using
| "<<" and ">>" in C. If "use integer" (see "Integer Arithmetic") is in
| force then signed C integers are used, else unsigned C integers are
| used.
(perldoc perlop)

So (-1 >> 1) uses unsigned integer operations. (unsigned)-1 is
guaranteed to be the largest unsigned integer, so (-1 >> 1) is half of
that which is half the largest signed integer (unless there are unused
bits in the implementation, which C allows, but that's exceedingly
rare).

Even if there are padding bits, the C Standard defines the unsigned
right-shift E1 >> E2 as the integral part of E1 / (2**E2). The result
must be a valid unsigned integer. As a result, padding bits cannot
change the meaning of a bitshift operation.

Phil
 
P

Peter J. Holzer

C doesn't define[1] what happens if you right-shift a negative value.

But Perl does, at least if don't "use integer":

| Note that both "<<" and ">>" in Perl are implemented directly using
| "<<" and ">>" in C. If "use integer" (see "Integer Arithmetic") is in
| force then signed C integers are used, else unsigned C integers are
| used.
(perldoc perlop)

So (-1 >> 1) uses unsigned integer operations. (unsigned)-1 is
guaranteed to be the largest unsigned integer, so (-1 >> 1) is half of
that which is half the largest signed integer (unless there are unused
bits in the implementation, which C allows, but that's exceedingly
rare).

Even if there are padding bits, the C Standard defines the unsigned
right-shift E1 >> E2 as the integral part of E1 / (2**E2). The result
must be a valid unsigned integer.

Yes but not necessarily a valid signed integer. More specifically

UINT_MAX / 2 == INT_MAX

doesn't have to be true. An unsigned int may use more or less bits for
representing the value than a signed int.
As a result, padding bits cannot change the meaning of a bitshift
operation.

This isn't about the meaning but about the result. UINT_MAX >> 2 is
always the same as UINT_MAX / 2, but this is not necessarily the same as
INT_MAX.

hp
 
P

Peter J. Holzer

Doh, what was I thinking. Of course.



In C, the value passed to printf using formatter %d must be an integer (signed or not).
So the result of the division, a float, must be coerced to integer:
printf ("%d", (int)(18446744073709551615/2) );
Does Perl do this coersion in C or does it do its own rounding then chopping via
custom methods?

Since perl is written in C, it obviously does it "in C", but how exactly
the conversion is done I don't know (and I'm currently too lazy to check
the source). My guess is that it is a simple cast to unsigned int:

printf "%d\n", 1E100

prints -1 on my systems which is the same as

printf("%d\n", (unsigned int)1E100);

in C. Since this result is arguably wrong I wouldn't rely on it -
somebody might someday fix it.

I guess the IEEE 87 standard is 53 bit mantissa so it can't handle a 64 bit
(-1) u_integer division, but would handle 32 bit u_integers. That explains
how printf ("%d", 4294967295/2); works, but how can the previous poster's
64 bit result be -9223372036854775808 which is almost correct?

I think I explained that. What wasn't clear about the explanation?
As an aside, I wonder if Perl is sometimes compiled to take advantage of
faster SSE-2 64-bit in lieu of precision 80 bit.

The gcc manual says about SSE instructions:

| For the x86-64 compiler, these extensions are enabled by default.

So, it you are using perl on a 64 bit Linux system, it will (almost
certainly) use SSE instructions. The speed advantage is probably small -
interpreter overhead is likely to be much greater than the time actually
spent in FP arithmetic.
Also, I thought cpu's do integer arithmetic +-/*

CPUs do what they are told.

hp
 
Ad

Advertisements

P

Philip Potter

C doesn't define[1] what happens if you right-shift a negative value.

But Perl does, at least if don't "use integer":

| Note that both "<<" and ">>" in Perl are implemented directly using
| "<<" and ">>" in C. If "use integer" (see "Integer Arithmetic") is in
| force then signed C integers are used, else unsigned C integers are
| used.
(perldoc perlop)

So (-1 >> 1) uses unsigned integer operations. (unsigned)-1 is
guaranteed to be the largest unsigned integer, so (-1 >> 1) is half of
that which is half the largest signed integer (unless there are unused
bits in the implementation, which C allows, but that's exceedingly
rare).

Even if there are padding bits, the C Standard defines the unsigned
right-shift E1 >> E2 as the integral part of E1 / (2**E2). The result
must be a valid unsigned integer.

Yes but not necessarily a valid signed integer. More specifically

UINT_MAX / 2 == INT_MAX

doesn't have to be true. An unsigned int may use more or less bits for
representing the value than a signed int.

That's a very good point. I guess this is why C provides those constants
in the first place :)

Phil
 

Top