How to do binary addition in bash - bash

I am trying to add two 32 bit binary numbers. One of them is a constant (address_range_in_binary) , and another one is an element of an array (IPinEachSubnet[$val])
I am trying to follow the instructions here, but I could not figure out how to get it done using variables. I have been trying to use different combinations of the below, but none of them seems to work. It is probably a simple syntax issue. Any help would be appreciated. The following is printing some negative random values.
For example, if the values are as following:
$address_range_in_binary=00001010001101110000101001000000
$IPinEachSubnet[$val]=00000000000000000000000000010000
echo "ibase=2;obase=2;$((address_range_in_binary+IPinEachSubnet[$val]))" | bc -l
The output of this is -1011101110111111110

bash only solution
y=2#00001010001101110000101001000000
t=2#00000000000000000000000000010000
oct=$(printf '%o' $(( y + t ))) # no bin format in printf
o2b=({0..1}{0..1}{0..1})
r=''
for (( i=0; i<${#oct}; i++ ))
do
r+=${o2b[${oct:$i:1}]}
done
echo $r
the conversion from oct to bin is inspired in Bash shell Decimal to Binary conversion

Let's define your variables (I will use shorter names):
$ y=00001010001101110000101001000000
$ t=00000000000000000000000000010000
Now, let's run the command in question:
$ echo "ibase=2;obase=2;$((y+t))" | bc -l
-1011101110111111111
The above produces that incorrect result that you observed.
To get the correct result:
$ echo "ibase=2;obase=2; $y+$t" | bc -l
1010001101110000101001010000
Discussion
The command $((y+t)) tells bash to do the addition assuming that the numbers are base-10. The result of bash's addition is passed to bc. This is not what you want: You want bc to do the addition.
Using an array
$ y=00001010001101110000101001000000
$ arr=(00000000000000000000000000010000)
$ echo "ibase=2;obase=2; $y+${arr[0]}" | bc -l
1010001101110000101001010000

Related

Arithmetic operation and for loop in terminal [duplicate]

Here is my script:
d1=0.003
d2=0.0008
d1d2=$((d1 + d2))
mean1=7
mean2=5
meandiff=$((mean1 - mean2))
echo $meandiff
echo $d1d2
But instead of getting my intended output of:
0.0038
2
I am getting the error Invalid Arithmetic Operator, (error token is ".003")?
bash does not support floating-point arithmetic. You need to use an external utility like bc.
# Like everything else in shell, these are strings, not
# floating-point values
d1=0.003
d2=0.0008
# bc parses its input to perform math
d1d2=$(echo "$d1 + $d2" | bc)
# These, too, are strings (not integers)
mean1=7
mean2=5
# $((...)) is a built-in construct that can parse
# its contents as integers; valid identifiers
# are recursively resolved as variables.
meandiff=$((mean1 - mean2))
Another way to calculate floating numbers, is by using AWK rounding capability, for example:
a=502.709672592
b=501.627497268
echo "$a $b" | awk '{print $1 - $2}'
1.08218
In case you do not need floating point precision, you may simply strip off the decimal part.
echo $var | cut -d "." -f 1 | cut -d "," -f 1
cuts the integer part of the value. The reason to use cut twice is to parse integer part in case a regional setting may use dots to separate decimals and some others may use commas.
Edit:
Or, to automate the regional settings one may use locale.
echo $var | cut -d $(locale decimal_point) -f 1
You can change the shell which you are using. If you are executing your script with bash shell bash scriptname.sh try using ksh for your script execution. Bash doesn't support arithmetic operations that involve floating point numbers.
Big shout-out to the bc command - it totally saved my day! It's a simple answer, but it worked like a charm.
a=1.1
b=1.1
echo $a + $b | bc -l
# Output:
2.2
#SUM
sum=$(echo $a + $b | bc -l)
echo $sum
# Output
2.2
bc is a command-line calculator, which allows users to perform mathematical calculations on the terminal.

bash command line arithmetic echo $((x-y)) how to divide result by z?

I'm trying to figure out a way to take the result of a basic equation and divide by another integer. So in in simple form it looks like;
A-B = C and then divide C by D
I understand the first part of the equation is echo $((A-B)) but how do I get that result divided by D?
Any feedback would be greatly appreciated!
Using shell
This (taken from the comments) fails because the parentheses are unbalanced:
$ echo $((2147483633-807279114))/5184000))
Try instead:
$ echo $(( (2147483633-807279114)/5184000 ))
258
The above returns an integer because the shell only does integer arithmetic.
Using bc
If you want accurate floating-point numbers, the standard tool to use is bc:
$ echo '(2147483633-807279114)/5184000' | bc -l
258.52710628858024691358
Using python
Python supports both integer and floating point arithmetic:
$ python -c 'print (2147483633-807279114)//5184000'
258
$ python -c 'print (2147483633-807279114)/5184000.0'
258.527106289
Python, with its numpy extension, is an excellent tool even if your needs extend to complex scientific calculations.
Using awk
awk, a standard unix tool, supports floating-point math:
$ awk 'BEGIN{print (2147483633-807279114)/5184000;quit}'
258.527
You can do calculations inside $(( ))
If you want to calculate (A-B)/C, you can put that inside $(( )):
echo $(( (A-B)/C ))
Note that the result will be truncated towards zero, because bash does not support decimals.
Remember to make sure that you have $(( before the expression and )) after, and that all the parentheses inside match up.
dc <<< "$A $B - $D / p"
Note 1: p prints the final result
Note 2: With dc you need to first enter the numbers and then operation

Negative number in bash shell scripting

I need some help with a bash mathematical expression script. I need the expression below to be a negative 2 to the power of 63 but I have tried all sorts of combinations from "", '', (()) and even keeping the positive value but multiplying it by -1 but I can't seem to get the syntax right.
Original expression:
kw=`expr 2^63 | bc`
gives me 9223372036854775808 (which is correct) but I need to get a -9223372036854775808.
I want to be able to do a kw=expr -2^63 | bc to get a negative results but it's not as straight-forward as I'd hoped. Hence my numerous attempts of different permutations to the expression.
Any help will be very appreciated.
Here you go:
$ kw=$(echo -2^63 | bc)
$ echo $kw
-9223372036854775808
UPDATE
#DigitalTrauma is right, if you're in bash, then using a bash here string is better (one less process, more efficient):
kw=$(bc <<< -2^63)
Since this is bash, you don't even need the echo; you can use a bash here string instead:
$ kw=$(bc <<< -2^63)
$ echo $kw
-9223372036854775808

Raise to the power in shell

How do you raise m to the power of n? I've searched for this everywhere. What I found was that writing m**n should work, but it doesn't. I'm using #!/bin/sh.
I would try the calculator bc. See http://www.basicallytech.com/blog/index.php?/archives/23-command-line-calculations-using-bc.html for more details and examples.
eg.
$ echo '6^6' | bc
Gives 6 to the power 6.
Using $n**$m really works indeed.
Maybe you don't use the correct syntax to evaluate a math expression.
Here's how I get the result on Bash:
echo $(($n**$m))
or
echo $[$n**$m]
The square brackets here are not meant to be like the test evaluator in the if statement so you can you use them without spaces too. I personally prefer the former syntax with round brackets.
using bc is an elegant solution. If you want to do this in bash:
$ n=7
$ m=5
$ for ((i=1, pow=n; i<m; i++)); do ((pow *= n)); done
$ echo $pow
16807
$ echo "$n^$m" | bc # just to verify the answer
16807
You might use dc. This
dc -e "2 3 ^ p"
yields
8
My system admin didn't install dc so adding to other correct answers, I bet you have not thought of this -
a=2
b=3
python -c "print ($a**$b)"
>> 8
works in bash/shell.
#! /bin/bash
echo "Enter the number to be done"
n=2
read m
let P=( $n**$m )
echo "The answer is $p"
ANSWER
Enter the number to be done
3
The answer is 8

Shell loops using non-integers?

I wrote a .sh file to compile and run a few programs for a homework assignment. I have a "for" loop in the script, but it won't work unless I use only integers:
#!/bin/bash
for (( i=10; i<=100000; i+=100))
do
./hw3_2_2 $i
done
The variable $i is an input for the program hw3_2_2, and I have non-integer values I'd like to use. How could I loop through running the code with a list of decimal numbers?
I find it surprising that in five years no one ever mentioned the utility created just for generating ranges, but, then again, it comes from BSD around 2005, and perhaps it wasn't even generally available on Linux at the time the question was made.
But here it is:
for i in $(seq 0 0.1 1)
Or, to print all numbers with the same width (by prepending or appending zeroes), use -w. That helps prevent numbers being sent as "integers", if that would cause issues.
The syntax is seq [first [incr]] last, with first defaulting to 1, and incr defaulting to either 1 or -1, depending on whether last is greater than or less than first. For other parameters, see seq(1).
you can use awk to generate your decimals eg steps of0.1
num=$(awk 'BEGIN{for(i=1;i<=10;i+=0.1)print i}')
for n in $num
do
./hw3_2_2 $n
done
or you can do it entirely in awk
awk 'BEGIN{cmd="hw3_2_2";for(i=1;i<=10;i+=0.1){c=cmd" "i;system(cmd) } }'
The easiest way is to just list them:
for a in 1.2 3.4 3.11 402.12 4.2 2342.40
do
./hw3_2_2 $a
done
If the list is huge, so you can't have it as a literal list, consider dumping it in a file and then using something like
for a in $(< my-numbers.txt)
do
./hw3_2_2 $a
done
The $(< my-numbers.txt) part is an efficient way (in Bash) to substitute the contents of the names file in that location of the script. Thanks to Dennis Williamson for pointing out that there is no need to use the external cat command for this.
Here's another way. You can use a here doc to include your data in the script:
read -r -d '' data <<EOF
1.1
2.12
3.14159
4
5.05
EOF
for i in "$data"
do
./hw3_2_2 "$i"
done
Similarly:
array=(
1.1
2.12
3.14159
4
5.05
)
for i in "${array[#]}"
do
./hw3_2_2 "$i"
done
I usually also use "seq" as per the second answer, but just to give an answer in terms of a precision-robust integer loop and then bc conversion to a float:
#!/bin/bash
for i in {2..10..2} ; do
x=`echo "scale=2 ; ${i}/10" | bc`
echo $x
done
gives:
.2
.4
.6
.8
1.0
bash doesn't do decimal numbers. Either use something like bc that can, or move to a more complete programming language. Beware of accuracy problems though.

Resources