Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Perl > Perl Misc > Identify if a scalar is int, double or text

Reply
Thread Tools

Identify if a scalar is int, double or text

 
 
Klaus
Guest
Posts: n/a
 
      05-11-2007
Hello,

I have a list of 4 scalars
my @L = (3, '3', 3.0, '3.0');

The first is obviously an int, the second is text, the third a double
and the last is text again.

I want to write a subroutine type_id which returns either 'int',
'double', 'text' (or '?') for each of the scalars, so that

print type_id($_), ' ' for (@L); print "\n";

results in the following output:

int text double text

I have found a solution where I use Devel:eek, call Dump(), redirect
STDERR into an "in-memory" file ( \$data ) and analyse the "in-memory"
content:

$data =~ /^SV = IV(0x/ # for ints
$data =~ /^SV = NV(0x/ # for doubles
$data =~ /^SV = PV(0x/ # for text

but I wanted to know whether there is a better way.

************************************************** ********
Here is my solution:

use strict;
use warnings;

use Devel:eek;

print STDERR "Beginning of program\n";

my @L = (3, '3', 3.0, '3.0');

print type_id($_), ' ' for (@L);
print "\n";

print STDERR "End of program\n";

sub type_id {

# ============================
# At first I could not get the "in-memory"
# working. It took me a while before I
# found the all important documentation
# in perldoc -f open:
#
# [...] if you try to re-open STDOUT or
# STDERR as an "in memory" file, you have
# to close it first [...]
#
# As a consequence, a simple
# local *STDERR;
# open STDERR, '>', \$data;
# does not work.
#
# The following redirects STDERR into an
# "in-memory" file, but leaves STDERR
# closed on exit:
# local *STDERR = *STDERR;
# close STDERR;
# open STDERR, '>', \$data;
#
# so we have to dup STDERR first and
# restore STDERR manually at the end
# (...knowing that if the restore fails,
# we won't have STDERR anymore):
# ============================

# dup STDERR
open my $olderr, '>&', \*STDERR
or return "?002 [dup STDERR: $!]";
close STDERR
or return "?005 [close STDERR: $!]";

my $data = '';
open STDERR, '>', \$data;
Dump $_[0];
close STDERR;

# restore STDERR
open STDERR, '>&', $olderr or die;

if ($data =~ m{^SV = (.V)}) {
if ($1 eq 'IV') { return 'int' }
if ($1 eq 'NV') { return 'double' }
if ($1 eq 'PV') { return 'text' }
return "?020 [Invalid: SV = $1]";
}
return "?030 [Err: ".substr($data, 0, 12)."]";
}

--
Klaus

 
Reply With Quote
 
 
 
 
Sisyphus
Guest
Posts: n/a
 
      05-11-2007

"Klaus" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed) oups.com...
> Hello,
>
> I have a list of 4 scalars
> my @L = (3, '3', 3.0, '3.0');
>
> The first is obviously an int, the second is text, the third a double
> and the last is text again.
>
> I want to write a subroutine type_id which returns either 'int',
> 'double', 'text' (or '?') for each of the scalars, so that
>
> print type_id($_), ' ' for (@L); print "\n";
>
> results in the following output:
>
> int text double text
>
> I have found a solution where I use Devel:eek, call Dump(), redirect
> STDERR into an "in-memory" file ( \$data ) and analyse the "in-memory"
> content:
>
> $data =~ /^SV = IV(0x/ # for ints
> $data =~ /^SV = NV(0x/ # for doubles
> $data =~ /^SV = PV(0x/ # for text
>
> but I wanted to know whether there is a better way.


There's probably already a module that does this. (Scalar-Util-Numeric might
be one such module.)
It's fairly straightforward with XS or Inline::C. Be aware that a variable
can change from one type to another in rather sneaky ways - as the following
demonstrates:

use warnings;
use Inline C => Config =>
BUILD_NOISY => 1;

use Inline C => <<'END_OF_C_CODE';

int _itsa (SV * x) {
if(SvIOK(x)) return 1;
if(SvNOK(x)) return 2;
if(SvPOK(x)) return 3;
}

END_OF_C_CODE

my @L = (3, '3', 3.0, '3.0');
print type_id($_), ' ' for (@L); print "\n";

# int text double text

my $string = '7';
print type_id($string), "\n"; # text
$string *= 1;
print type_id($string), "\n"; # int

my $var = ~0;
print type_id($var), "\n"; # int
$var *= -1;
print type_id($var), "\n"; # double (though that might
# differ on 64-bit perls)

sub type_id {
return "int" if (_itsa($_[0]) == 1);
return "double" if(_itsa($_[0]) == 2);
return "text" if(_itsa($_[0]) == 3);
return "unknown";
}

 
Reply With Quote
 
 
 
 
anno4000@radom.zrz.tu-berlin.de
Guest
Posts: n/a
 
      05-11-2007
Klaus <(E-Mail Removed)> wrote in comp.lang.perl.misc:
> Hello,
>
> I have a list of 4 scalars
> my @L = (3, '3', 3.0, '3.0');
>
> The first is obviously an int, the second is text, the third a double
> and the last is text again.
>
> I want to write a subroutine type_id which returns either 'int',
> 'double', 'text' (or '?') for each of the scalars, so that
>
> print type_id($_), ' ' for (@L); print "\n";
>
> results in the following output:
>
> int text double text


It is very rare in Perl that you need to know these differences.

> I have found a solution where I use Devel:eek, call Dump(), redirect
> STDERR into an "in-memory" file ( \$data ) and analyse the "in-memory"
> content:


[snip]

You can tell the difference between a number or a string from
the behavior of bitwise boolean operations:

print $_ & ~$_ ? 'str ' :'num ' for (3, '3', 3.0, '3.0');
print "\n";

There is no similar way to tell the difference between an integer
and a float that happens to have an integer value.

The B::* set of modules should have the means to get the info
without catching printed output.

Anno
 
Reply With Quote
 
Mirco Wahab
Guest
Posts: n/a
 
      05-11-2007
Klaus wrote:
> Hello,
>
> I have a list of 4 scalars
> my @L = (3, '3', 3.0, '3.0');
>
> The first is obviously an int, the second is text, the third a double
> and the last is text again.
>
> I want to write a subroutine type_id which returns either 'int',
> 'double', 'text' (or '?') for each of the scalars, so that
>
> print type_id($_), ' ' for (@L); print "\n";
>
> results in the following output:
>
> int text double text


As Sisyphus mentioned, that might be tricky and
not very reliable, because Perl 'DWYM'-ifies
it's scalars every time depending on the context.

The situations where you would really need this
detailed information would also (imho) justify
looking deep into the perl (as Sisyphus did with
XS code or with the B modules).

After reading Anno's idea (bitwise complement), I
tried to combine this with Scalar::Util::Numeric
by a quick hack:

...
use Scalar::Util::Numeric qw(isnum isint isfloat);
no strict 'refs';

for my $l (3, '3', 3.0, '3.0', 'abc') {
print "[ $l ]\tlooks like ", $l & ~$l ? 'string' :'number', ' | ';
print map "$_, ", grep $_->($l), qw' isint isfloat ' ;
print "\n"
}
...

which prints:

[ 3 ] looks like number | isint,
[ 3 ] looks like string | isint,
[ 3 ] looks like number | isint,
[ 3.0 ] looks like string | isfloat,
[ abc ] looks like string |


Just an idea ...

Regards

M.
 
Reply With Quote
 
Klaus
Guest
Posts: n/a
 
      05-11-2007
On May 11, 2:49 pm, (E-Mail Removed)-berlin.de wrote:

> Klaus <(E-Mail Removed)> wrote in comp.lang.perl.misc:
> > I have a list of 4 scalars
> > my @L = (3, '3', 3.0, '3.0');
> > I want to write a subroutine type_id which returns either 'int',
> > 'double', 'text' (or '?') for each of the scalars
> > I have found a solution where I use Devel:eek, call Dump(), redirect
> > STDERR into an "in-memory" file ( \$data ) and analyse the "in-memory"
> > content:


[snip]

> The B::* set of modules should have the means to get the info
> without catching printed output.


That's it - it's the B module which I was looking for (I knew it
existed, but I never thought I would ever use it).

Thanks a million !!

I am now using a simple "use B qw(svref_2object class);" and my
program is now much smaller and more to the point without any
redirection of STDERR:

Here is my new program:
============================
use strict;
use warnings;

use B qw(svref_2object class);

my @L = (3, '3', 3.0, '3.0');

print type_id($_), ' ' for (@L);
print "\n";

sub type_id {
my $cl = class svref_2object \$_[0];
if ($cl eq 'IV') { return 'int' }
if ($cl eq 'NV') { return 'double' }
if ($cl eq 'PV') { return 'text' }
return "?020 [Invalid: class(SV-Object) = '$cl']";
}
============================

--
Klaus

 
Reply With Quote
 
Klaus
Guest
Posts: n/a
 
      05-11-2007
On May 11, 2:23 pm, "Sisyphus" <(E-Mail Removed)> wrote:

> "Klaus" <(E-Mail Removed)> wrote in message
> > I want to write a subroutine type_id which returns either 'int',
> > 'double', 'text' (or '?') for each of the scalars


> Be aware that a variable can change from one type to
> another in rather sneaky ways - as the following
> demonstrates:


[ snip ]

> # int text double text
>
> my $string = '7';
> print type_id($string), "\n"; # text
> $string *= 1;
> print type_id($string), "\n"; # int
>
> my $var = ~0;
> print type_id($var), "\n"; # int
> $var *= -1;
> print type_id($var), "\n"; # double (though that might
> # differ on 64-bit perls)


I see your point.

Here is another example of how a variable can change from one type to
another in rather sneaky ways:

=============================
use strict;
use warnings;

use B qw(svref_2object class);

my $num = 1;
while (1) {
my $id = type_id($num);
printf "id = %-6s num = %s\n", $id, $num;
last if $id ne 'int';
$num *= 10;
}

while ($num > 1) {
$num /= 10;
my $id = type_id($num);
printf "id = %-6s num = %s\n", $id, $num;
}

sub type_id {
my $cl = class svref_2object \$_[0];
if ($cl =~ m{IV$}) { return 'int' }
if ($cl =~ m{NV$}) { return 'double' }
if ($cl =~ m{PV$}) { return 'text' }
return "[$cl]";
}
=============================

And here is the output:

=============================
id = int num = 1
id = int num = 10
id = int num = 100
id = int num = 1000
id = int num = 10000
id = int num = 100000
id = int num = 1000000
id = int num = 10000000
id = int num = 100000000
id = int num = 1000000000
id = double num = 10000000000
id = double num = 1000000000
id = double num = 100000000
id = double num = 10000000
id = double num = 1000000
id = double num = 100000
id = double num = 10000
id = double num = 1000
id = double num = 100
id = double num = 10
id = double num = 1
=============================

--
Klaus

 
Reply With Quote
 
anno4000@radom.zrz.tu-berlin.de
Guest
Posts: n/a
 
      05-11-2007
Klaus <(E-Mail Removed)> wrote in comp.lang.perl.misc:
> On May 11, 2:49 pm, (E-Mail Removed)-berlin.de wrote:
>
> > Klaus <(E-Mail Removed)> wrote in comp.lang.perl.misc:
> > > I have a list of 4 scalars
> > > my @L = (3, '3', 3.0, '3.0');
> > > I want to write a subroutine type_id which returns either 'int',
> > > 'double', 'text' (or '?') for each of the scalars
> > > I have found a solution where I use Devel:eek, call Dump(), redirect
> > > STDERR into an "in-memory" file ( \$data ) and analyse the "in-memory"
> > > content:

>
> [snip]
>
> > The B::* set of modules should have the means to get the info
> > without catching printed output.

>
> That's it - it's the B module which I was looking for (I knew it
> existed, but I never thought I would ever use it).
>
> Thanks a million !!


Glad I could help, but I'm asking myself why you need to
know these differences. Perl works very hard to let us deal with
integers, floats, strings and references in a unified way. In
particular, the difference of a float and an int is usually
irrelevant. If it matters to your program, you are probably
not doing something the Perl way.

Anno
 
Reply With Quote
 
anno4000@radom.zrz.tu-berlin.de
Guest
Posts: n/a
 
      05-11-2007
Mirco Wahab <(E-Mail Removed)> wrote in comp.lang.perl.misc:
> Klaus wrote:
> > Hello,
> >
> > I have a list of 4 scalars
> > my @L = (3, '3', 3.0, '3.0');
> >
> > The first is obviously an int, the second is text, the third a double
> > and the last is text again.


> After reading Anno's idea (bitwise complement), I


I got that slightly wrong.

my $type = $_ & ~$_ ? 'str' : 'num';

takes an empty string for a number.

my $type = !length || $_ & ~$_ ? 'str' : 'num';

gets it right.

Anno
 
Reply With Quote
 
Klaus
Guest
Posts: n/a
 
      05-12-2007
On May 11, 11:07 pm, (E-Mail Removed)-berlin.de wrote:
> Klaus <(E-Mail Removed)> wrote in comp.lang.perl.misc:
>
> > On May 11, 2:49 pm, (E-Mail Removed)-berlin.de wrote:

>
> > > Klaus <(E-Mail Removed)> wrote in comp.lang.perl.misc:
> > > > I have a list of 4 scalars
> > > > my @L = (3, '3', 3.0, '3.0');
> > > > I want to write a subroutine type_id which returns either 'int',
> > > > 'double', 'text' (or '?') for each of the scalars
> > > > I have found a solution where I use Devel:eek, call Dump(), redirect
> > > > STDERR into an "in-memory" file ( \$data ) and analyse the "in-memory"
> > > > content:

>
> > [snip]

>
> > > The B::* set of modules should have the means to get the info
> > > without catching printed output.

>
> > That's it - it's the B module which I was looking for (I knew it
> > existed, but I never thought I would ever use it).

>
> > Thanks a million !!

>
> Glad I could help, but I'm asking myself why you need to
> know these differences. Perl works very hard to let us deal with
> integers, floats, strings and references in a unified way. In
> particular, the difference of a float and an int is usually
> irrelevant. If it matters to your program, you are probably
> not doing something the Perl way.


I agree with that.

But I have a case where I need more control over the way Perl manages
numerical values.

At one point in the future, I will certainly have to read "perldoc
perlxstut" to gain understanding of how I can use C to control
numerical values, but being on Windows XP / Activestate Perl 5.8.8
with next to no background in C, I thought I tackle the problem first
with whatever tool / module / function Perl is giving me.

Here is my case:

I have written a Perl program where some of the variables have a
special requirement, that is they deal exclusively with monetary Euro
values (i.e. the requirement is that calculations happens in normal
IEEE float/double arithmetic, and assignment to any special "monetary"
variable must be rounded to the 2nd decimal).

My first approach was to use sprintf("%.2f", $var) whenever I assign
values to a "monetary Euro" variable, but this falls foul of the way
numerical values are stored internally (i.e. 0.10 can not be
represented as an exact double).

I worked around that by storing integer "cents" rather than "Euros": I
use sprintf("%.0f", $var) when I assign "monetary" values.

That works fine, but I found out that a "monetary" value above 20
million Euros ( > 2^31 cents ) is not stored as an integer, but as a
double. As I said, it seems to work and the double seems to stick to
its "integer" properties, but I want to monitor very closely what
happens with that "double" if I grow bigger and bigger values.

My final solution would be to tie variables to some C-subroutines that
(efficiently) do 4 things when I assign values to that variable:

1. Round values to the nearest integer (same as sprintf "%.0f", but
more efficient).
2. If that value fits into an int (i.e. -2^31 <= val <= 2^31-1), then
it must always be stored as an int.
3. If it is stored as a double, then make sure that double has no
decimal.
4. If a value is too big to be represented as a double with no
decimals, then
4a.) either set the variable to the maximum integral "double"
value,
4b.) or, depending on a compile time option, die.

-- Klaus


>
> Anno



 
Reply With Quote
 
Dr.Ruud
Guest
Posts: n/a
 
      05-12-2007
Klaus schreef:

> That works fine, but I found out that a "monetary" value above 20
> million Euros ( > 2^31 cents ) is not stored as an integer, but as a
> double. As I said, it seems to work and the double seems to stick to
> its "integer" properties, but I want to monitor very closely what
> happens with that "double" if I grow bigger and bigger values.


Never heared of bigints?
http://search.cpan.org/search?query=bigint&mode=all

--
Affijn, Ruud

"Gewoon is een tijger."
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
How to identify double bytes language? sqlcamel Perl Misc 8 11-14-2009 11:46 PM
double -> text -> double Ole Nielsby C++ 11 11-30-2006 05:16 PM
cannot convert parameter from 'double (double)' to 'double (__cdecl *)(double)' error Sydex C++ 12 02-17-2005 06:30 PM
Replace scalar in another scalar Mark Perl Misc 4 01-27-2005 02:48 PM
Shorthand for($scalar) loops and resetting pos($scalar) Clint Olsen Perl Misc 6 11-13-2003 12:50 AM



Advertisments