Comparing integers and decimals in bash [duplicate] - bash

This question already has answers here:
How can I compare two floating point numbers in Bash?
(22 answers)
Closed 7 years ago.
I am trying to compare two numbers stored in variables in a shell script. The objective is to find if the incoming value is inside the specified ranges: 500-600 or 700-800.
My current code looks like:
inc=0
srange[0]=500
erange[0]=600
srange[1]=700
erange[1]=800
for i in "${startrange[#]}"; do
if [[ " ${startrange[$inc]} " -lt " $actual " ]]; then
echo " in range "
fi
inc = $((inc+1))
done
The code works with integer values such as 530 or 540 but fails with decimal values like 530.2 or 540.3, resulting in a syntax error on this line of the code:
if (( " ${startrange[$inc]} " -lt " $actual " )); then
How can I fix this to handle the decimal values?

The shell can only do integer calculations. If you want to compare values that are non-integers, you must use a tool that can understand decimal math. bc is one such option:
if bc <<< "${startrange[inc]} < $actual"; then …

It seems like you're saying that your code works for integer numbers but not for decimal numbers.
In that case you could simply turn all your numbers into integers before you do the comparison to avoid that error.
This is one way to turn decimals to integers in bash:
DECIMAL=3.14
INTEGER=${DECIMAL%.*}
Note that if you used this code to try to turn an integer into an integer it would return the same number so you can apply this to any number you get regardless of whether or not it is a decimal.

Related

How do I sort a Perl hash by keys numerically? [duplicate]

This question already has answers here:
How can I sort a hash's keys naturally?
(4 answers)
Closed 1 year ago.
My first question ... I have found many answers to other questions through search but I am failing to do so this time :-)
I want to generate a report that is sorted by a number that is embedded in a string from my input data. The report is being generate from elements of a perl hash where the same number is used as the hash key.
The output that I am currently getting is sorted like strings.
foreach my $num (sort keys %dir_map) {
$path = $paths{$num};
$name = $names{$num};
printf OUT ("%d %s %s\n",$num,$path,$name);
}
My input data looks like:
dist_14 randomString nameStringIwant RandomInteger AnotherRandomString
Which I am processing like:
while(<IN>) {
chomp;
my #header = split /\s+/;
my $header_length = $#header ;
if ( /dist_/ ) {
my $NumberStr = $header[0] ;
$justNumberStr =~ s/dist_//;
my $justNumber = sprintf("%d",$justNumberStr);
$names{$justNumber} = $header[2];
}
}
As you've discovered, Perl's sort will, by default, sort using a string comparison. To override that default behaviour, you need to provide a sort block.
foreach my $num (sort { $a <=> $b } keys %dir_map)
The sort block is given two of the elements from your list in the variables $a and $b. Your code should compare these two values and return a negative integer if $a comes before $b, a positive integer if $b comes before $a and zero if they sort in the same place. The "spaceship operator" (<=>) does exactly that for two numbers.
The FAQ How do I sort an array by (anything)?
might also be useful. You don't have an array, but your list of keys can be treated in the same way.

Bash error message: syntax error near unexpected token '|'

I was creating a program that calculates the area of circle, but bash doesnt compile and execute due to error message in the title. Here is my code:
elif [ $num -le 6 ] && [ $num -ge 4 ]
then
read -p "Enter radius: " radius
let areaCirc=("scale=2;3.1416 * ($radius * $radius)"|bc)
echo "Area of the circle is: " $areaCirc
and the error message is:
syntax error near unexpected token '|'
can someone help me?
To send a string to a command via stdin, use a here-string command <<< string, not a pipe.
Command substitution syntax is $(...), not (...).
Don't use let here. Shell arithmetic only supports integers.
areaCirc=$(bc <<< "scale=2;3.1416 * ($radius * $radius)")
let provides arithmetic context, but we have an ambiguity here, because in a let expression, the vertical bar (|) means bitwise or, but in the shell it has also the meaning of a pipe operator. Look at the following examples:
let bc=4
let a=(4+bc) # Sets a to 8
let b=("5+bc") # Sets b to 9
let c=("(2+4)|bc")
This is syntactically correct and sets c to 6 (because 2+4 equals 6, and the bitwise or of 6 and 4 equals 6).
However if we decided to quote only part of the argument, i.e.
let c=("(2+4)"|bc)
or don't quote at all, i.e.
let c=((2+4)|bc)
we get a syntax error. The reason is that the shell, when parsing a command, first separates it into the different commands, which then are strung together. The pipe is such an operator, and the shell thinks that you want to do a let areaCirc=("scale=2;3.1416 * ($radius * $radius)" and pipe the result into bc. As you can see, the let statement is uncomplete; hence the syntax error.
Aside from this, even if you would fix it, your using of let would not work, because you are using a fractional number (3.1416), and let can do only integer arithmetic. As a workaround, either you do the whole calculation using bc, or some other language (awk, perl,...), or, if this is an option, you switch from bash to zsh, where you can do floating point arithmetic in the shell.

What does the # symbol mean in this bash for loop? [duplicate]

I know that one can get the length of an array in bash by doing ${#arrayname[#]}.
My question is: is this just something that I have to memorize, or can this syntax be broken down into understandable parts? For instance, what does the # symbol mean where one would expect to find the index? Why the #?
# at the beginning of a variable reference means to get the length of the variable's value. For a normal variable this means its length in characters. # is the "number" sign, so you can remember this as meaning "the number of things in the variable".
# or * in an array index means to use the whole array, not a specific element, and instead of returning the number of characters, it returns the number of array elements. * is used as a wildcard in many contexts, so this should be easy to remember. Also, $* and $# are used to mean all the arguments to a shell script, so the parallel with all the array elements should be obvious.
You can't just write ${#arrayname} because when you use an array variable without a subscript, it's equivalent to element 0 of the array. So ${#arrayname} is the same as ${#arrayname[0]}, which is the number of characters in the first element of the array.
You should memorize. :) The # usually means number. e.g. the
$# - is the number of arguments
${#str} - length of the string $str
${#arr[#]}" - length (number of elements) of the array arr
${#arr} - the length of the 1st element of the array (like the str above)
Unfortunately the ${parameter#word} or ${parameter##word} has nothing with numbers. (it removes the shortest/longest word from the beginning of the parameter.
And also, the # .... is comment ;)
In general usage of form ${#PARAMETER} returns the length in number of characters and NOT bytes of the parameter's value.
myString="Hello StackOverflow!"
printf "%s\n" "${#myString}"
20
But for arrays, this expansion type has two meanings:
For individual elements, it reports the string length of the element
(as for every "normal" parameter)
For the mass subscripts # and * it
reports the number of set elements in the array
Consider an example over arrays,
myArray=(1 2 3 4 15)
printf "%s\n" "${myArray[#]}" # <-- Gives me list of elements
1
2
3
4
15
printf "%s\n" "${#myArray[#]}" # <-- Gives me number of elements
5
It gets interesting now, the length of the last element 2 can be obtained by doing
printf "%s\n" "${#myArray[4]}"
2
The '#' acts the same way as '*'. Instead of providing a specific index this references the full thing.
The '#' is telling bash you want the length
https://www.cyberciti.biz/faq/finding-bash-shell-array-length-elements/

Bash, need to count variables, round to 2 and store to variable [duplicate]

This question already has answers here:
Round a divided number in Bash
(10 answers)
How to display number to two decimal places in bash function
(4 answers)
Closed 3 years ago.
I need to count variables, round to 2 and store to variable.
Example:
I have variable from array ${array[5]} and vat variable defined, I need to calculate simple
( $vat * ${array[5]} + ${array[5]} )
and store to variable pricevat.
I tried:
vat = 0.21
pricevat=$(echo "$vat * ${array[5]}" + ${array[5]} | bc -l)
(( pricevat=$vat*${array[5]}+${array[5]}))
But nothing works:
line 48: ((: pricevat=0.21*0.233+0.233: syntax error: invalid arithmetic operator (error token is ".21*0.233+0.233"
Could you help me please? Where is the problem? What is best solution for this. Thank you very much.
S.
On possibility (though, it will not round, but truncate to 3 decimal places):
array=( ... ... ... ... ... 102.03 ... )
vat=0.21
pricevat=$(bc <<< "scale=3; (1+$vat)*${array[5]}")
The trick is to have bc do the rounding, using its special variable scale, set to 3.
Yes, it is working! I did it like this.
Arithmetic operations:
pricevat=$(echo "$vat * ${array[5]}" + ${array[5]} | bc -l)
Round to 3 places:
pricevat=$(printf "%0.3f\n" $pricevat)
If there is another way to do it better or together on one line, let me know please.
Thanks.

Force Ruby to not output a float in standard form / scientific notation / exponential notation

I have the same problem as is found here for python, but for ruby.
I need to output a small number like this: 0.00001, not 1e-5.
For more information about my particular problem, I am outputting to a file using f.write("My number: " + small_number.to_s + "\n")
For my problem, accuracy isn't that big of an issue, so just doing an if statement to check if small_number < 1e-5 and then printing 0 is okay, it just doesn't seem as elegant as it should be.
So what is the more general way to do this?
f.printf "My number: %.5f\n", small_number
You can replace .5 (5 digits to the right of the decimal) with any particular formatting size you like, e.g., %8.3f would be total of 8 digits with three to the right of the decimal, much like C/C++ printf formatting strings.
If you always want 5 decimal places, you could use:
"%.5f" % small_number
I would do something like this so you can strip off trailing zero's:
puts ("%.15f" % small_number).sub(/0*$/,"")
Don't go too far past 15, or you will suffer from the imprecision of floating point numbers.
puts ("%.25f" % 0.01).sub(/0*$/,"")
0.0100000000000000002081668
This works also on integers, trim excess zeros, and always returns numbers as a valid floating point number. For clarity, this uses the sprintf instead of the more cryptic % operator.
def format_float(number)
sprintf('%.15f', number).sub(/0+$/, '').sub(/\.$/, '.0')
end
Examples:
format_float(1) => "1.0"
format_float(0.00000001) => "0.00000001"

Resources