Differences between revisions 66 and 72 (spanning 6 versions)
Revision 66 as of 2009-12-30 18:07:08
Size: 3364
Editor: MatthiasPopp
Comment:
Revision 72 as of 2012-11-12 18:28:00
Size: 4470
Editor: 95
Comment: Added another "bc" use, which seems to be easier to remember, uses 6 fewer parenthesis and doesn't need a subshell. Please, feel free to correct me. Thanks for the Bash FAQ. :)
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:
Line 3: Line 4:
[[BASH]] does not have built-in floating point [[ArithmeticExpression|arithmetic]]: [[BASH]]'s builtin [[ArithmeticExpression|arithmetic]] uses integers only:
Line 6: Line 7:
    $ echo $((10/3))
    3
$ echo $((10/3))
3
Line 9: Line 10:

Ba
sh cannot do ''anything'' with floating point numbers, ''including'' compare them to each other(*). Instead, an external program must be used, e.g. {{{bc}}}, {{{awk}}} or {{{dc}}}:
For most operations involving floating-point numbers, an external program must be used, e.g. `bc`, [[AWK]] or `dc`:
Line 13: Line 13:
    $ echo "scale=3; 10/3" | bc
    3.333
$ echo "scale=3; 10/3" | bc
3.333
Line 16: Line 16:
The "scale=3" command notifies `bc` that three digits of precision after the decimal point are required.
Line 17: Line 18:
The "scale=3" command notifies {{{bc}}} that three digits of precision after the decimal point are required.

Same example with {{{dc}}} (reversed polish calculator, lighter than bc):
{{{
    $ echo "3 k 10 3 / p" | dc
}}}

{{{k}}} sets the precision to 3, and {{{p}}} prints the value of the top of the stack with a newline. The stack is not altered, though.

If you are trying to compare floating point numbers, be aware that a simple ''x < y'' is not supported by all versions of {{{bc}}}.
Same example with `dc` (reversed polish calculator, lighter than `bc`):
Line 29: Line 21:
    # This would work with some versions, but not HP-UX 10.20.
    # The here string feature, inherited from rc->zsh->ksh93 was
    # introduced in bash 2.05b-alpha1
    imadev:~$ bc <<< '1 < 2'
    syntax error on line 1,
$ echo "3 k 10 3 / p" | dc
3.333
Line 35: Line 24:
`k` sets the precision to 3, and `p` prints the value of the top of the stack with a newline. The stack is not altered, though.
Line 36: Line 26:
Alternatively, you could use this: If you are trying to compare floating point numbers (less-than or greater-than), and you have GNU `bc`, you can do this:
Line 39: Line 29:
    # Bash
    if [[ $(bc <<< "1.4 - 2.5") = -* ]]; then
       echo "1.4 is less than 2.5."
    fi
# Bash
if (( $(bc <<< "1.4 < 2.5") )); then
  echo "1.4 is less than 2.5."
fi
Line 44: Line 34:

This example subtracts 2.5 from 1.4, and checks the sign of the result. If it is negative, the first number is less than the second.

Portable version:
or
Line 50: Line 37:
    # Bourne
    c
ase "`echo "1.4 - 2.5" | bc`" in
      -*) echo "1.4 is less than 2.5";;
    esac
# Bash
if
bc <<< "1.4 < 2.5" >/dev/null; then
  echo "1.4 is less than 2.5."
fi
Line 55: Line 42:
However, ''x < y'' is not supported by all versions of `bc`:
Line 56: Line 44:
{{{
# This would work with some versions, but not HP-UX 10.20.
imadev:~$ bc <<< '1 < 2'
syntax error on line 1,
}}}
If you want to be portable, you need something more subtle:

{{{
# POSIX
case $(echo "1.4 - 2.5" | bc) in
  -*) echo "1.4 is less than 2.5";;
esac
}}}
This example subtracts 2.5 from 1.4, and checks the sign of the result. If it is negative, the first number is less than the second. We aren't actually treating `bc`'s output as a number; we're treating it as a string, and only looking at the first character.

Legacy (Bourne) version:

{{{
# Bourne
case "`echo "1.4 - 2.5" | bc`" in
  -*) echo "1.4 is less than 2.5";;
esac
}}}
Line 59: Line 70:
    $ awk 'BEGIN {printf "%.3f\n", 10 / 3}'
    3.333
$ awk 'BEGIN {printf "%.3f\n", 10 / 3}'
3.333
Line 62: Line 73:
Line 65: Line 75:
Newer versions of zsh and the KornShell have built-in floating point arithmetic, together with mathematical functions like {{{sin()}}} or {{{cos()}}} . Newer versions of zsh and the KornShell have built-in floating point arithmetic, together with mathematical functions like {{{sin()}}} or {{{cos()}}}.  So many of these calculations can be done natively in ksh:
Line 67: Line 77:
Caveat: Many problems that look like floating point arithmetic can in fact be solved using integers only, and thus do not require these tools: Problems dealing with rational numbers. For example, to check if two numbers {{{x}}} and {{{y}}} are in a ratio of 4:3 or 16:9 you may use something along these lines:
Line 69: Line 78:
# ksh93
$ echo $((3.00000000000/7))
0.428571428571428571
}}}
Comparing two floating-point numbers for ''equality'' is actually an unwise thing to do; two calculations that should give the same result on paper may give ever-so-slightly-different floating-point numeric results due to rounding/truncation issues. If you wish to determine whether two floating-point numbers are "the same", you may either:

 * Round them both to a desired level of precision, and then compare the rounded results for equality; or
 * Subtract one from the other and compare the absolute value of the difference against an ''epsilon'' value of your choice.

One of the very few things that Bash actually ''can'' do with floating-point numbers is round them, using `printf`:

{{{
# Bash 3.1
# See if a and b are close to each other.
# Round each one to two decimal places and compare results as strings.
a=3.002 b=2.998
printf -v a1 %.2f $a
printf -v b1 %.2f $b
if [[ $a1 = "$b1" ]]; then echo "a and b are the same, roughly"; fi
}}}
Caveat: Many problems that look like floating point arithmetic can in fact be solved using integers only, and thus do not require these tools -- e.g., problems dealing with rational numbers. For example, to check whether two numbers {{{x}}} and {{{y}}} are in a ratio of 4:3 or 16:9 you may use something along these lines:

{{{
# Bash
Line 80: Line 113:
(*)Actually, I lied. It can print them, using {{{printf}}} and one of the {{{%e}}} or {{{%f}}} or {{{%g}}} format strings. But that's all.

How can I calculate with floating point numbers instead of just integers?

BASH's builtin arithmetic uses integers only:

$ echo $((10/3))
3

For most operations involving floating-point numbers, an external program must be used, e.g. bc, AWK or dc:

$ echo "scale=3; 10/3" | bc
3.333

The "scale=3" command notifies bc that three digits of precision after the decimal point are required.

Same example with dc (reversed polish calculator, lighter than bc):

$ echo "3 k 10 3 / p" | dc
3.333

k sets the precision to 3, and p prints the value of the top of the stack with a newline. The stack is not altered, though.

If you are trying to compare floating point numbers (less-than or greater-than), and you have GNU bc, you can do this:

# Bash
if (( $(bc <<< "1.4 < 2.5") )); then
  echo "1.4 is less than 2.5."
fi

or

# Bash
if bc <<< "1.4 < 2.5" >/dev/null; then
  echo "1.4 is less than 2.5."
fi

However, x < y is not supported by all versions of bc:

# This would work with some versions, but not HP-UX 10.20.
imadev:~$ bc <<< '1 < 2'
syntax error on line 1,

If you want to be portable, you need something more subtle:

# POSIX
case $(echo "1.4 - 2.5" | bc) in
  -*) echo "1.4 is less than 2.5";;
esac

This example subtracts 2.5 from 1.4, and checks the sign of the result. If it is negative, the first number is less than the second. We aren't actually treating bc's output as a number; we're treating it as a string, and only looking at the first character.

Legacy (Bourne) version:

# Bourne
case "`echo "1.4 - 2.5" | bc`" in
  -*) echo "1.4 is less than 2.5";;
esac

AWK can be used for calculations, too:

$ awk 'BEGIN {printf "%.3f\n", 10 / 3}'
3.333

There is a subtle but important difference between the bc and the awk solution here: bc reads commands and expressions from standard input. awk on the other hand evaluates the expression as part of the program. Expressions on standard input are not evaluated, i.e. echo 10/3 | awk '{print $0}' will print 10/3 instead of the evaluated result of the expression.

Newer versions of zsh and the KornShell have built-in floating point arithmetic, together with mathematical functions like sin() or cos(). So many of these calculations can be done natively in ksh:

# ksh93
$ echo $((3.00000000000/7))
0.428571428571428571

Comparing two floating-point numbers for equality is actually an unwise thing to do; two calculations that should give the same result on paper may give ever-so-slightly-different floating-point numeric results due to rounding/truncation issues. If you wish to determine whether two floating-point numbers are "the same", you may either:

  • Round them both to a desired level of precision, and then compare the rounded results for equality; or
  • Subtract one from the other and compare the absolute value of the difference against an epsilon value of your choice.

One of the very few things that Bash actually can do with floating-point numbers is round them, using printf:

# Bash 3.1
# See if a and b are close to each other.
# Round each one to two decimal places and compare results as strings.
a=3.002 b=2.998
printf -v a1 %.2f $a
printf -v b1 %.2f $b
if [[ $a1 = "$b1" ]]; then echo "a and b are the same, roughly"; fi

Caveat: Many problems that look like floating point arithmetic can in fact be solved using integers only, and thus do not require these tools -- e.g., problems dealing with rational numbers. For example, to check whether two numbers x and y are in a ratio of 4:3 or 16:9 you may use something along these lines:

# Bash
# Variables x and y are integers
if (( $x*9-$y*16==0 )) ; then
   echo "16:9."
elif (( $x*3-$y*4==0 )) ; then
   echo "4:3."
else
   echo "Neither 16:9 nor 4:3."
fi

A more elaborate test could tell if the ratio is closest to 4:3 or 16:9 without using floating point arithmetic. Note that this very simple example that apparently involves floating point numbers and division is solved with integers and no division. If possible, it's usually more efficient to convert your problem to integer arithmetic than to use floating point arithmetic.


CategoryShell

BashFAQ/022 (last edited 2021-09-01 06:31:58 by geirha)