Why do lt and gt in Perl not work for comparing real numbers? - perl

Lets examine the following perl code
if ($a lt 0.00 or $a gt 100.000)
{
print "a must be between 0 and 100 \n";
exit 1
}
exit 0
Lets say a equals 5. The above code will exit with failure status because a isn't between 0 and 100.
Simply replacing the lt and gt with the actual operators they represent, < and > respectively, yields the expected results. Replacing the 100 with a number starting with a 9 will also yield the expected result.
Why are Perl's comparison operators telling me 5 is not between 0 and 100?

lt and gt are string operators, with numbers you want to use plain old < and >. Perl is polymorphic on values, so it's monomorphic on operators (unlike for example python which is the other way around).

In perl the lt and gt operators are not the same as the < and >. the perl documentation details this here perlop under rational operators, below is extracted from the documentation:
Binary "<" returns true if the left argument is numerically less than the right argument.
Binary ">" returns true if the left argument is numerically greater than the right argument.
Binary "<=" returns true if the left argument is numerically less than or equal to the right argument.
Binary ">=" returns true if the left argument is numerically greater than or equal to the right argument.
Binary "lt" returns true if the left argument is stringwise less than the right argument.
Binary "gt" returns true if the left argument is stringwise greater than the right argument.
Binary "le" returns true if the left argument is stringwise less than or equal to the right argument.
Binary "ge" returns true if the left argument is stringwise greater than or equal to the right argument.
Since perl does not have a string object and integer object perl has to make a guess at the context of the object. The only way perl can know if you you are comparing a string or an integer is by ensuring that the rational operators for lt and gt force the context for comparisons as a sting and that < and > operators for the context for comparisons as integers

Related

Compare if one string is greater than another in kdb

I want to check which of the two strings in greater, for which I have was using below logic but it fails in few cases
q){$[1b in x>=y;x;y]}["b";"b"]
"b"
q){$[1b in x>=y;x;y]}["c";"b"]
"c"
q){$[1b in x>=y;x;y]}["azz";"dff"] // Wrong output (Reason for failure - "azz">"dff" --> 011b)
"azz" / desired output dff
Please suggest another way to get the greatest string of the provided strings?
Since comparison operator compares character by character hence in "azz" and "dff" if the output can be displayed as "dff" only after comparison for "d" from "dff" and "a" from "azz" as "a" is less than "d".
You can convert the string to symbol and use <, >, etc.. These operators perform lexicographic comparisons for symbols.
https://code.kx.com//q4m3/4_Operators/
q) `azz < `dff
1b
If you insist on strings then you can leverage iasc to creat a "smaller-or-equal"-like function:
q) not first iasc ("azz"; "dff")
1b
Using 1b in is equivalent to any in this case as "azz">"dff" equates to 011b. Your conditional will evaluate true as 2 letters in "azz" are greater than "dff".
It is better to cast x and y to symbols and compare as this will evaluate with 1 boolean:
(`$"azz")>=`$"dff"
0b
{$[(`$x)>=`$y;x;y]}["azz";"dff"]
"dff"
Alternatively you could sort desc order and take the first result:
{first desc(x;y)}["azz";"dff"]
"dff"

Is the truthiness of a dualvar always that of its string part?

The empirical behaviour of my Perl 5.26.2 x64 (Cygwin) is that a dualvar is truthy if and only if its string part is truthy:
# Falsy number, truthy string => truthy
$ perl -MScalar::Util=dualvar -E 'my $v=dualvar 0, "foo"; say "yes" if $v'
yes
# Truthy number, falsy string => falsy
$ perl -MScalar::Util=dualvar -E 'my $v=dualvar 1, ""; say "yes" if $v'
# Truthy number, truthy string => truthy
$ perl -MScalar::Util=dualvar -E 'my $v=dualvar 1, "foo"; say "yes" if $v'
yes
# Falsy number, falsy string => falsy
$ perl -MScalar::Util=dualvar -E 'my $v=dualvar 0, ""; say "yes" if $v'
This has been the case since 2009 per this.
Question: Is this guaranteed behaviour?
Boolean::String says that this is the behaviour. However, I don't know if that's something I can rely on, in terms of backward compatibility.
I also do not see an express statement in perlsyn, Scalar::Util, or perldata#Context.
I do see the following in perldata#Scalar-values:
A scalar value is interpreted as FALSE in the Boolean sense if it is undefined, the null string or the number 0 (or its string equivalent, "0"), and TRUE if it is anything else. The Boolean context is just a special kind of scalar context where no conversion to a string or a number is ever performed.
The statement that "no conversion ... is ever performed" unfortunately doesn't tell me which part(s) of a dualvar the interpreter is looking at!
Similarly, Chas. Owens's related answer says that
the truthiness test looks at strings first
But if it looks at strings first, what does it look at second, and when?
Edit My understanding is that if overload is defined on a variable, dualvar or not, the bool overload will control. I am wondering about the non-overloaded case.
Edit 2 ikegami's answer here points out that PL_sv_yes and PL_sv_no also have an NV (double) component. For bonus points :) , does the NV have any effect on truthiness if a dualvar has one? (Let me know if that answer is actually involved enough to deserve a separate question.)
Yes, at least so far. The SvTRUE_common macro is usually used to decide where an SV is "true" in a boolean context. Here's how it is defined in sv.h in the perl 5.26.1 source:
#define SvTRUE_common(sv,fallback) ( \
!SvOK(sv) \
? 0 \
: SvPOK(sv) \
? SvPVXtrue(sv) \
: (SvFLAGS(sv) & (SVf_IOK|SVf_NOK)) \
? ( (SvIOK(sv) && SvIVX(sv) != 0) \
|| (SvNOK(sv) && SvNVX(sv) != 0.0)) \
: (fallback))
After the scalar passes the SvOK test (whether it is defined), the next check is SvPOK -- whether the scalar has a valid internal string representation. Dualvars always pass this check, so the boolean test of a dualvar is whether its string representation is true (SvPVXtrue(...)).
The code is different in perl 5.6.2
I32
Perl_sv_true(pTHX_ register SV *sv)
{
if (!sv)
return 0;
if (SvPOK(sv)) {
register XPV* tXpv;
if ((tXpv = (XPV*)SvANY(sv)) &&
(tXpv->xpv_cur > 1 ||
(tXpv->xpv_cur && *tXpv->xpv_pv != '0')))
return 1;
else
return 0;
}
else {
...
but the logic is the same -- check SvPOK first and then return whether the string representation is not empty and not equal to "0".
I would think future generations of Perl developers would be wary of changing this long-standing logic.
Question: Is this guaranteed behaviour?
This boils down to how a scalar is tested in the Boolean context, as string or numeric?
In Perl the documentation is the closest thing to a standard. So if there is no statement in docs then the formal answer must be: No, it is not "guaranteed behaviour".
Since the docs come tantalizingly close a few times, talking about that context and conversions, and yet specifically do not spell out which test is done I'd say that this must indeed be taken as an implementation detail. You cannot "rely" on it.
If strict reliability is needed one solution is a simple class that ensures to test what you need.
In more practical terms, it appears that in if ($v) it is the string part that is tested, and if it's not there then a numeric test goes (without the actual conversion as the docs say). As you ask about variables that have been set as dualvar then for those it's going to be the string test.

Perl comparison operator output

I am not exactly sure what the output of a comparison is. For instance, consider
$rr = 1>2;
$qq = 2>1;
print $rr; #nothing printed
print $qq; #1 printed
Is $rr the empty string? Is this behavior documented somewhere? Or how can one tell for sure?
I was looking for the answer in Learning Perl by Schwartz et al., but could not immediately resolve the answer.
http://perldoc.perl.org/perlop.html#Relational-Operators:
Perl operators that return true or false generally return values that can be safely used as numbers. For example, the relational operators in this section and the equality operators in the next one return 1 for true and a special version of the defined empty string, "" , which counts as a zero but is exempt from warnings about improper numeric conversions, just as "0 but true" is.
So it what is returned is something that is an empty string in string context, and 0 in numeric context.

perl how to force boolean to 0/1

I'm trying to get a 0 or 1 in a value for true/false. Here's the code:
use strict;
my %h = (Y => "y");
my $bool_x = 1 & exists $h{X};
my $bool_y = 1 & exists $h{Y};
print("x $bool_x y $bool_y\n");
I needed to add the "1 &" to force it to not be the empty string. Is there a better way to do this? I realize that it's an artifact of the way perl prints the false value, but I need it to be a 0 not the empty string.
The most efficient way to do this is just:
$bool || 0
Your code is far from clear. The & operator is a bitwise operator that behaves differently on numbers and strings, but exists returns a value that will obligingly be the number zero or the empty string depending on what is required of it. perldoc perlop has this to say
Although no warning is currently raised, the result is not well defined when this operation is performed on operands that aren't either numbers (see Integer Arithmetic) nor bitstrings (see Bitwise String Operators)
So because the result of exists is one such value your code is on shakey ground
There is no need to write something arcane, and if you want to represent Perl's internal true and false values by a different pair of values then the conditional operator is the correct choice
my $bool_x = exists $h{X} ? 1 : 0
I believe that's the most readable without being verbose, and that's all that matters here. It's also applicable to any other pair of values that you may choose, such as
my $bool_x = exists $h{X} ? 'Y' : 'N'
There are many ways to "numify" a true/false value.
If $var can contain 1 or any false value, all of these will evaluate to either "0" or "1":
0 + $var
0 | $var
$var || 0
1 * $var
1 & $var (you discovered this one, already)
chr(48+$var)
sprintf "%d", $var
These constructions return a 0/1 value when $var can contain any true or false value:
0 + !!$var ( !! true => 1, !! false => "" )
1 - !$var
$var ? 1 : 0

0, 0e0, 0.0, -0, +0, 000 all mean the same thing to Perl, why?

Just puzzling to me.
Related, but different question:
What does “0 but true” mean in Perl?
Perl doesn't distinguish kinds of numbers. Looking at all of those with a non-CS/programmer eye, they all mean the same thing to me as well: zero. (This is one of the foundations of Perl: it tries to work like people, not like computers. "If it looks like a duck....")
So, if you use them as numbers, they're all the same thing. If you use them as strings, they differ. This does lead to situations where you may need to force one interpretation ("0 but true"; see also "nancy typing"). but by and large it "does the right thing" automatically.
I don't understand, what else should they mean?
You give integer, scientific, floating point, signed integers and octal notations of zero. Why should they differ?
0==0 as everyone, including Larry Wall, knows.
Perl interprets every scalar value as both a string and (potentially) a number. All of those string representations of zero can convert to the integer value 0 , according to perl's conversion rules:
"0", "0.0", "-0", "+0", "000" => Simplest case of straight string to numeric conversion.
"0e0" => In a numeric context, only the leading valid numeric characters are converted, so only the leading "0" is used. For example, "1984abcdef2112" would be interpreted numerically as 1984.
"0 but true" in perl means that a string like "0e0" will evalutate numerically to 0, but in a boolean context will be "true" because the conversion to boolean follows different rules than the strict numeric conversion.
Perl works in contexts. In string context, they are all different. In numeric context, they are all zero.
print "same string\n" if '0' eq '0.0';
print "same number\n" if 0 == 0.0;
'0 but true' in boolean context is true:
print "boolean context\n" if '0 but true';
print "string context\n" if '0 but true' eq '0';
print "numeric context\n" if '0 but true' == 0;