Search code examples
bashdecimal-pointsquare-root

Calculate Square root to decimal places in bash


I am writing a simple bash script to calculate a square root to 3 decimal places by default but the user can set the number of places... Nothing complex, I just iterate from 1 to the lowest square root. Since my bash is still basic this is what I came up with.

#!/usr/bin/env bash

num=${1}
places=${2-3}
i=0

while [[ $(( i*i )) -lt ${num} ]]    // <= The problem should be here
do
    i=$(( i + 1 ))
done

echo ${i};
rem=$(( num % i ))
root="${i}."
for (( j=0; j<places; j++ ))
do
    rem=$((rem * 10))
    root="$root$((rem / i))"
    rem=$((rem % i))
done
echo ${root}

But due to some reason, it won't produce the correct result for a wide range of numbers like bashfile.sh 9 // will produce 3.000 but bashfile.sh 8 will return 3.666 kindly help, what is wrong with while [[ ]]


Solution

  • Here is a fairly simple, although not very efficient solution:

    #!/bin/bash
    
    num=$1
    prec=${2:-3}
    
    for (( i=0; i < prec; ++i )); do let num*=100; done
    
    for (( sqrt=0; sqrt*sqrt <= num; ++sqrt )); do :; done
    let --sqrt
    
    sqrt="${sqrt:0:-prec}.${sqrt: -prec}"
    echo $sqrt
    

    To answer your question ("what is wrong with while [[ ]]"), your comparison was slightly incorrect. Instead of -lt (ie, <) it should have been <=. And you needed to subtract 1 from i after the loop to get the correct whole part of the square root.

    I couldn't make sense of the second part of your code that calculates decimal places. However, a simple solution is to instead pre-multiply the original number to get needed precision, which is what I've done above.


    UPDATE

    As @dash-o has pointed out if you are using bash, chances are you have either bc or awk installed, which are better suited for this:

    num=$1
    prec=${2:-3}
    
    # using bc
    echo "scale=$prec; sqrt($num)" | bc
    
    # using awk
    awk "BEGIN {printf \"%.${prec}f\n\", sqrt($num)}"