bash array multiplication using bc

I am trying to multiply array values with values derived from the multiplication of a loop index using bc.

#!/bin/bash
n=10.0
bw=(1e-3 2.5e-4 1.11e-4 6.25e-5 4.0e-5 2.78e-5 2.04e-5 1.56e-5 1.29e-5 1.23e-5 1.0e-5)
for k in {1..11};do
    a=$(echo "$n * $k" | bc)
    echo "A is  $a"
    arn=${bw[k-1]}
    echo "Arn is  $arn"
    b=$(echo "$arn * $a" | bc -l)
    echo "b is $b"
    #echo $a $b
done

I am able to echo the array values by assigning it to a new variable within the loop, but when I use that to multiply using bc, I get (standard_in) 1: syntax error.
I searched for clues and tried some but none helped. The expected output is as follows.

10  1.00E-02
20  5.00E-03
30  3.33E-03
40  2.50E-03
50  2.00E-03
60  1.67E-03
70  1.43E-03
80  1.25E-03
90  1.16E-03
100 1.23E-03
110 1.10E-03

All help is greatly appreciated.

Asked By: csnl

||

bc doesn’t support the scientific format. Use something that does.

For example, Perl:

    a=$(perl -e "print $n * $k" )
    arn=${bw[k-1]}
    b=$(perl -e "printf '%.2E', $arn * $a")
    echo $a $b

Output:

10 1.00E-02
20 5.00E-03
30 3.33E-03
40 2.50E-03
50 2.00E-03
60 1.67E-03
70 1.43E-03
80 1.25E-03
90 1.16E-03
100 1.23E-03
110 1.10E-03
Answered By: choroba

Replacing OP’s for loop with a single awk script:

awk -v n="$n" '                             # assign awk variable "n" the value of OS variable "n"
{ k++                                       # increment counter (initially == 0)
  printf "%s %0.2En",(k*n),($1*k*n)        # print output
}
' < <(printf "%sn" "${bw[@]}")             # print each bw[] entry on a separate line

This generates:

10 1.00E-02
20 5.00E-03
30 3.33E-03
40 2.50E-03
50 2.00E-03
60 1.67E-03
70 1.43E-03
80 1.25E-03
90 1.16E-03
100 1.23E-03
110 1.10E-03
Answered By: markp-fuso

bc doesn’t support scientific notation, but you could replace e with *10^ to get the result.

$ printf 'print k+=10, " ", k*%s, "n"n' "${bw[@]/[eE]/*10^}" | bc -l
10 .01000000000000000000
20 .00500000000000000000
30 .00333000000000000000
40 .00250000000000000000
50 .00200000000000000000
60 .00166800000000000000
70 .00142800000000000000
80 .00124800000000000000
90 .00116100000000000000
100 .00123000000000000000
110 .00110000000000000000

(here assuming GNU bc for its print extension).

If the point of using bc is that you want arbitrary precision, you could use perl‘s bignum module for that:

$ perl -Mbignum -e 'for $i (@ARGV) {printf "%-3d %.2En", $k += 10, $i * $k}' -- "${bw[@]}"
10  1.00E-02
20  5.00E-03
30  3.33E-03
40  2.50E-03
50  2.00E-03
60  1.67E-03
70  1.43E-03
80  1.25E-03
90  1.16E-03
100 1.23E-03
110 1.10E-03

If you don’t need arbitrary precision, then it would be easier to switch to a shell with floating point support like zsh, ksh93 or yash.

A lot of the syntax in that script such as array=(...) or {1..11} actually come from zsh, borrowed by bash years later.

#! /bin/zsh -
bw=(
  1e-3     2.5e-4   1.11e-4  6.25e-5
  4.0e-5   2.78e-5  2.04e-5  1.56e-5
  1.29e-5  1.23e-5  1.0e-5
)
k=0
for i ($bw) printf '%-3d %.2En' k+=10 'k*i'
Answered By: Stéphane Chazelas
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.