if [ 3 -lt 6 ]; then
echo "It works with ints"
fi
if [ 3.0 -lt 6.0 ]; then
echo "It works with floats"
else
echo "It doesn't work with floats"
fi
Comparing the integers in an "if" works just fine.
But it doesn't work when I do the same thing with floating point numbers, and gives me this output:
+ [ 3.0 -lt 6.0 ]
/tmp/hudson7259224562322746826.sh: 11: [: Illegal number: 3.0
+ echo It doesn't work with floats
It doesn't work with floats
As per the bash manpage (my emphasis at the end):
arg1 OP arg2 : OP is one of -eq, -ne, -lt, -le, -gt, or -ge. These arithmetic binary operators return true if arg1 is equal to, not equal to, less than, less than or equal to, greater than, or greater than or equal to arg2, respectively. Arg1 and arg2 may be positive or negative integers.
In other words, bash has no native support in comparing floating point values.
If you really want to handle floating point, you're better off choosing a tool that does support it, such as bc:
n1=3.0 ; n2=6.0
if [[ $(echo "${n1} < ${n2}" | bc) -eq 1 ]] ; then
echo ${n1} is less than ${n2}
fi
Related
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.
I am using Powershell and need to extract the digits before the decimal point so that I can evaluate the number extracted
So with $Interest_Rate = 15.5
I have tried the following code .. but they do not work:
$Interest_RatePart1 = "{0:N0}" -f $Interest_Rate
It rounds the value to 16
$Interest_RatePart1 = ($Interest_Rate -split '.')[0].trim()
It returns a blank.
I just want to return 15
Formatting the number will cause rounding away from zero
Use Math.Truncate() - which always rounds towards zero - instead:
$Interest_RatePart1 = [Math]::Truncate($Interest_Rate)
FWIW, the reason your last attempt returns nothing, is because -split defaults to regular expressions, and . means any character in regex.
Either escape the . with \:
$Interest_RatePart1 = ($Interest_Rate -split '\.')[0].Trim()
or specify that it shouldn't use regex:
$Interest_RatePart1 = ($Interest_Rate -split '.', 2, 'SimpleMatch')[0].Trim()
or use the String.Split() method instead:
$Interest_RatePart1 = "$Interest_Rate".Split('.')[0].Trim()
Mathias' [Math]::Truncate is correct - some other options for you though, pay attention to Floor as it is Slightly Different to Truncate when working with negative numbers.
Cast to int (can round up)
[int]$Interest_Rate
Use [Math]::Floor (will always round down, similar to truncate for non-negative numbers)
[Math]::Floor($Interest_Rate)
Use [Math]::Round with 0 decimal places. (can round up)
[Math]::Round($Interest_Rate, 0)
There isn't much documentation on fish. I need to know if
date '+%k' is greater than 8
How do I do it? Thanks.
You can use the test fish builtin, like:
if test (date +%k) -gt 8
...
end
test or [ are the way you would do this. This is true of all shells, since these are external commands.
if [ (date '+%k') -ge 8 ]
echo "It's larger!"
end
Unlike in traditional languages, the if operator in fish works on exit status of commands. Therefore an additional command test is used to translate logical/mathematical operations into exit status codes.
here are some examples:
count $argrv # returns 0
test (count $argrv) -eq 0 # returns 0, which is evaluated as TRUE by if
test (count $argrv) -eq 1 # returns 1, which is evaluated as FALSE by if
here's how you check if the hour is greater then 8
function testIf
if test (date '+%k') -gt 8
echo "yes"
else
echo "no"
end
end
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
I'm working on a Wake-On-Lan script and I have this code that is working I am just trying to understand it better.
Here is an excerpt that I am having trouble with:
$mac = "78ab78ab78ab" #some user input mac address reformatted to something like this one
$packet = [byte[]](,0xFF * 102)
6..101 |% { $packet[$_] = $mac[($_%6)] }
[byte[]](,0xFF * 102)
This is saying to make an array of byte values 0xFF 102 times correct? So packet[0] should equal 0xFF through packet[101] = 0xFF
6..101 |% { $packet[$_] = $mac[($_%6)] }
This is a loop that operates on an element of the $packets array we declared earlier based on the iterator ($_) that grows by one as it progresses through the loop. I'm not sure what the $_%6 does in particular the %6 operator. I have ran through the loop and output the value (which does change) but since I'm not familiar with the operator I'm not sure what the output really means.
I think your main question is around $_%6. You should read it as $_ % 6, where $_ and 6 are your operands, and % in Powershell is a binary Mod operator from VB.NET world. It returns a remainder of integer division. So, for example 5 Mod 2 = 1.