6

How do I get bc to start decimal fractions with a leading zero?

$ bc <<< 'scale=4; 1/3'
  .3333

I want 0.3333.

terdon
  • 242,166
Petr Skocik
  • 28,816
  • 2
    Possible duplicate of http://stackoverflow.com/questions/8402181/how-do-i-get-bc1-to-print-the-leading-zero – JRFerguson Jun 25 '16 at 17:09

5 Answers5

8

bc natively does not support adding zero.

Workaround is:

echo 'scale=4; 1/3' | bc -l | awk '{printf "%.4f\n", $0}'
0.3333
  • \n  – terminate the output with a newline.

  • %f  – floating point

  • %.4f – the number of digits to show.  This specifies 4 digits after the decimal point.

AnilV
  • 171
2

Unfortunately bc is written in such a way that you cannot. This statement also applies to its neighbor dc. People, as a solution, usually suggest using some text-processing tool to change bc output, or use completely different tool like perl or python which has different syntax, but to me they seems to be too heavy for such simple task.

From your other questions it looks that you are zsh user so I would suggest zcalc, which has very similar syntax for basic operations like bc:

$ zcalc -e '1/3'
0.333333

First you need to load this function (e.g. inside .zshrc) and probably you may want to always treat all numbers as a floating point with -f option:

autoload -U zcalc
alias zcalc='zcalc -f'

As a bonus, you can define special prompt for the calculator with ZCALCPROMPT parameter, for details look at man zshcontrib.

jimmij
  • 47,140
  • One day, when I get mad at this enough, I'll fix Linux pipelines to be executable fully in-process, sans execs, forks, and context switches, ending this stupid builtin/external dichotomy once and for all. – Petr Skocik Jun 25 '16 at 19:29
  • 1
    @PSkocik:  I’m curious what your plan is.    Do you plan to make every program in the world internal to the shell? – G-Man Says 'Reinstate Monica' Apr 10 '23 at 05:28
2

bc can be persuaded to use a leading zero:

for div in 10 1000; do 
  echo "scale=3;v=158/$div; if(v > -1 && v < 1) print 0,v,\"\n\" else print v, \"\n\"" | bc
done
Chris Davies
  • 116,213
  • 16
  • 160
  • 287
  • 2
    You check for v > -1, but that doesn't work for negative numbers as you insert the 0 before the sign (and get something like 0-.158 instead of -0.158). – Stéphane Chazelas Apr 09 '23 at 13:44
  • 1
    Also, if the result is zero, your routine will print 00. – G-Man Says 'Reinstate Monica' Apr 10 '23 at 02:31
  • Yes - I wrote that code fragment for a specific use-case after discovering the issue myself. Nothing like a problem to spur one to hack a solution. Following your comments I've written a small function that seems to address those and others. I'll post it as a new answer since it is completely different. – ImproviseAdaptOvercome Apr 23 '23 at 11:53
0

No need for awk if you have printf (either builtin to the shell, or as a separate program).

If you specifically want four digits to the right of the decimal point,

$ printf '%.4f\n'            "$(bc <<< 'scale=4; 1/7')"
0.1428

If you want to make it scriptable,

$ ndigits=4
$ printf '%.*f\n' "$ndigits" "$(bc <<< "scale=$ndigits; 1/7")"
0.1428

If you want to suppress trailing zeroes, use %g instead of %f.

$ printf '%.4g\n'   "$(bc <<< 'scale=4; 1/3')" \
                    "$(bc <<< 'scale=4; 1/4')" \
                    "$(bc <<< 'scale=4; 1/7')" \
                    "$(bc <<< 'scale=4; 1/8')"
0.3333
0.25
0.1428
0.125

But this works the way you want only if the value is between −1 and 1:

$ printf '%.4g\n'   "$(bc <<< 'scale=4; 1/7')" \
                    "$(bc <<< 'scale=4; 10/7')" \
                    "$(bc <<< 'scale=4; 100/7')" \
                    "$(bc <<< 'scale=4; 1000/7')"
0.1428
1.429
14.29
142.9

because the 4 here is interpreted as the total number of digits, not the number of digits to the right of the decimal point.

  • Note that with several printf implementations, that won't work if the locale decimal radix character is the comma instead of period as printf honours is but bc doesn't. – Stéphane Chazelas Apr 10 '23 at 06:18
0

This is a belt-and-braces solution that addresses the failures using my previous answer. This small function somewhat intelligently figures out when and where to stick the leading 0 and negative sign and uses the backspace control-code to move backwards in the printed value. It won't work in Posix -s|--standard mode (or if printed on a Teletype!):

#!/usr/bin/env bc
# this is lz.bc
define lz(d) {
  if (ibase != 10 || obase != 10) {
    print "(io)base must be decimal (10)\n"
    return -1
  }
  n=0; b=0; a=0
  if (d<0) n=1 # negative
  if (d>-1 && d<0) b=1 # below
  if (d>0 && d<1) a=1  # above
  print " ", d
  for (i=length(d)+1; i>=0; i--) {
    print "\b"
    if(i==0 && (b || a)) print "0\b\b"
  }
  if(n) print "-"
  print "\n"
  return 0
}

v[0]=-.234 v[1]=.234 v[2]=-2.345 v[3]=2.345 v[4]=-123.4567 v[5]=123.456 for(x=0;x<6;x++) { print v[x], " ... " r=lz(v[x]) } obase=16 r=lz(v[5]) quit

which when called displays:

$ bc lz.bc 
bc 1.07.1
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
-.234 ... -0.234
.234 ... 0.234
-2.345 ...  -2.345
2.345 ...  2.345
-123.4567 ...  -123.4567
123.456 ...  123.456
(io)base must be decimal (10)