14
#!/bin/bash 
q=$(bc <<< "scale=2;$p*100")
head -n$q numbers.txt > secondcoordinate.txt

That's just part of the script, but I think it's enough to clarify my intentions. p is a variable with just two decimals, so q should be an integer... Nevertheless, bc shows, for example, 10.00 instead of 10.

How can I solve this?

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
Diego
  • 309

5 Answers5

14

You can't do this with the obvious scale=0 because of the way that the scale is determined.

The documentation indirectly explains that dividing by one is sufficient to reset the output to match the value of scale, which defaults to zero:

expr1 / expr2 The result of the expression is the quotient of the two expressions. The scale of the result is the value of the variable scale.

p=12.34; echo "($p*100)" | bc
1234.00

p=12.34; echo "($p*100)/1" | bc 1234

If your version of bc does not handle this, pipe it through sed instead:

p=12.34; echo "($p*100)" | bc | sed -E -e 's!(\.[0-9]*[1-9])0*$!\1!' -e 's!(\.0*)$!!'
1234

This pair of REs will strip trailing zeros from the decimal part of a number. So 3.00 will reduce to 3, and 3.10 will reduce to 3.1, but 300 will remain unchanged.

Alternatively, use perl and dispense with bc in the first place:

p=12.34; perl -e '$p = shift; print $p * 100, "\n"' "$p"
Chris Davies
  • 116,213
  • 16
  • 160
  • 287
  • 2
    Perhaps using printf would be cleaner than piping through sed? e.g. printf '%.0f\n' $(bc <<< "$p*100") – steeldriver Nov 03 '16 at 00:36
  • @steeldriver that requires printf to interpret the string as a number and then reformat it. I'd have preferred to use the PCRE (\.[0-9]?*)0*$ (which would strip trailing zeros from values such as 12.3400) but sed can't handle that. – Chris Davies Nov 03 '16 at 00:38
5

you can use awk to calculate the values

bash-3.2$ p=0.01
bash-3.2$ q=$(awk -vp_val="$p" 'BEGIN{print p_val*100}')
bash-3.2$ echo $q
1


bash-3.2$ p=0.02
bash-3.2$ q=$(awk -vp_val="$p" 'BEGIN{print p_val*100}')
bash-3.2$ echo $q
2


bash-3.2$ p=0.022
bash-3.2$ q=$(awk -vp_val="$p" 'BEGIN{print p_val*100}')
bash-3.2$ echo $q
2.2
Kamaraj
  • 4,365
4

TL;DR

You have lots of options. bc has known behavior where scale=0 doesn't always do what you expect, but there are a lot of workarounds. Here are just a few.

printf

Use printf to limit your output to integers.

$ printf "%g\n" $(echo '12.34 * 100' | bc)
1234

bc with division

If you want to stick with bc scaling, you need to specify both a scale of zero and divide by 1 to reset the scale. This is known behavior, but I really can't explain the why of it.

$ echo '12.34 * 100 / 1' | scale=0 bc
1234

sed

Just strip off the unwanted trailing characters.

$ echo '12.34 * 100' | bc | sed 's/\.00$//'
1234

bash

Use a brace expansion to return the value before the decimal.

$ p='12.34'; q=$(bc <<< "scale=2; $p*100"); echo ${q%%.00}
1234
CodeGnome
  • 7,820
  • 1
    Just one niggle - ${q%%.00} would usually be referred to as parameter expansion rather than "brace expansion", I think – steeldriver Nov 03 '16 at 11:30
  • Using scale=0 bc sets the envvar scale which bc doesn't seem to read. It has to be included within the text piped to stdin. – jamadagni Dec 20 '19 at 04:17
2

I also had the same problem and wrote these 2 simple one-liners to clean the output:

define int(x) { auto z; z = scale; scale = 0; x /= 1; scale = z; return x; }
define n(x) { auto z, m, s;z = scale; m = x; while (int(m) != m) { m *= 10; s += 1; }; scale = s; x /= 1; scale = z; return x; }

Here int(x) turns a real number into an integer. It sets the scale to 0 and it divides the operand by 1 so the fractional part is cut off. n(x) will multiply the real number by 10 in a loop until the real number becomes an integer, i.e. all its fractional digits shifted to the left. s will match the number of fractional digits. So setting the scale to s and divide it by 1 will cut off the zeroes.

I've included these lines in a small lib.bc file containing all kinds of handy bc functions. Add the file as a parameter to bc to make these functions available (or, like myself, make an alias so every time you run bc you have them available)

You can use it like this:

echo "scale=20;2^-8"|bc lib.bc     # .00390625000000000000
echo "scale=20;n(2^-8)"|bc lib.bc  # .00390625
Chris Davies
  • 116,213
  • 16
  • 160
  • 287
aiso
  • 21
0

Here is a bash function to remove trailing zeroes.

remove_trailing_zeroes()
{
    declare -n n="$1"
    # Prepend a 0 if number starts with a dot.
    if [[ $n =~ ^[.] ]]; then
        n="0$n"
    fi
    # Remove trailing zeroes
    while [[ $n =~ [.].*0$ ]]; do
        n="${n%0}"
    done
    # Remove trailing dot if any
    if [[ $n =~ [.]$ ]]; then
        n="${n%.}"
    fi
}

You can then use it like this for your case:

q=$(bc <<< "scale=2;$p*100")
remove_trailing_zeroes q
head -n$q numbers.txt > secondcoordinate.txt
Frederic
  • 111
  • le sigh. This may work but it is a terrible approach to ops problem...and likely overkill for any problem. – jesse_b Aug 02 '19 at 14:56
  • @Jesse_b: I just copied a function I had created and used myself to solve a similar but more general problem. I don't see why you say it's a terrible or overkill approach. – Frederic Aug 02 '19 at 18:12