|
Guides All Guides and HOWTO's. |
|
Thread Tools | Display Modes |
|
|||
Simple human front-end for 'bc(1)', the unlimited precision calculator
A simple human front-end for 'bc(1)' the unlimited precision calculator.
Some weeks ago I got tired of dealing with inhumanely formatted big numbers like those presented by fdisk(1) of a 320 GB hard disk Code:
Disk: wd1 geometry: 38913/255/63 [625142448 Sectors] Offset: 0 Signature: 0xAA55 Starting Ending LBA Info: #: id C H S - C H S [ start: size ] ------------------------------------------------------------------------------- 0: A5 0 1 1 - 5220 254 63 [ 63: 83875302 ] FreeBSD *1: A6 5221 0 1 - 10441 254 63 [ 83875365: 83875365 ] OpenBSD 2: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused 3: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused To solve this issue I wrote a simple shell script with pre-processes input for 'bc(1)', and post-processes it's output. According to the man page, 'bc' is an arbitrary-precision arithmetic language and calculator. The 'bc' language supports variables, loops and functions. My goal was to only support simple single line calculations with Code:
- + : unary minus and plus + - * / % : addition, subtraction, multiplication, division and modulus The number format expected by 'bc' does not allow the usage of comma's or periods to group the digits in groups of three. Only a period '.' is allowed to specify a decimal fraction. The shell script 'calc' supports two number formats: 12,345,678.99, as used in the Anglo-Saxon world, and 12.345.678,99 as used in other parts of the world. The 12,345,678.99 I will call 'commified' and 12.345.678,99 'periodified'. Conversion of commified input for digestion by 'bc' is simple, just remove all comma's. A 'periodified' number needs two steps: first remove all periods '.', then change the lonely ',' into a '.' Output conversion Conversion of the result spit out by 'bc' is not that easy. The 'Perl cookbook' by Christiansen and presents a 'commify' perl script. My commented version of this script Code:
#!/usr/bin/perl -w # $Id: commify,v 1.1 2009/02/22 02:31:30 j65nko Exp $ use strict ; # Input style nr without ",'s : -155566.1234 # Output style : -1,555,666.1234 # taken from 'Perl Cookbook' # (\d\d\d)(?=/d) : group of 3 digits followed by another digit # (?!\d*\.) : the digits should not be followed by a decimal "." # -155566.1234 reversed : 4321.6665551- # ^^^^ group of 3 digits followed by another digit is candidate for # insertion of ','. Because it is followed by a decimal point, it is a decimal fraction, # and decimal fraction shouldn't be grouped. So the '(?!\d*\.)' makes sure this does not # happen # The first group of 3 digits is 666, which are followed by a '5'. # The regex engine just looks ahead for the 5, but will not consume it. # In the next iteration, it will start at this '5' and thus find '555' followed by the '1'. # So a ',' will be inserted. # The next iteration starting at the '1' finds a '-' , no other 2 digits for # sub commify { my $nr = reverse $_[0] ; $nr =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g ; return scalar reverse $nr ; } while (<>) { print commify $_ ; } This script also formed the inspiration of the 'periodify' script: Code:
#!/usr/bin/perl -w #$Id: periodify,v 1.1 2009/02/22 02:32:51 j65nko Exp $ use strict ; # inspired by 'commify' routine from 'Perl Cookbook' # first eliminate all ',' : 1,444,444.99 -> 1444444.99 # replace decimal "." by decimal ',' : 1444444.99 -> 1444444,99 # adds periods to group digits : 1.444.444,99 sub periodify { my $nr = reverse $_[0] ; $nr =~ s/,//g ; $nr =~ s/\./,/ ; $nr =~ s/(\d\d\d)(?=\d)(?!\d*,)/$1./g ; return scalar reverse $nr ; } while (<>) { print periodify $_ ; } Code:
#!/bin/sh # $Id: calc,v 1.2 2009/02/22 02:38:06 j65nko Exp $ case "$1" in # -p ) # clean 1.000.000,99 -> 1000000.99 as required by 'bc' CLEAN="sed -e s!\.!!g -e s!,!\.!g" FORMAT='periodify' # answer produced by 'bc' # replace decimal point by ',' and reinsert '.' for grouping ;; -c ) # clean 1,000,000.99 -> 1000000.99 as required by 'bc' CLEAN="sed -e s!,!!g" FORMAT='commify' # answer produced by 'bc' ;; *) echo "Usage: $0 -p (group digits with periods: '.')" echo " or" echo " $0 -c (group digits with commas : ',')" exit 1 esac while true ; do printf "Expression : " >&2 read EXPR if [ -z "${EXPR}" ] ; then break fi [ -t 0 ] || echo "${EXPR}" EXPR=$( echo "${EXPR}" | $CLEAN ) #printf "Normalized : $EXPR\n" ANSWER=$(echo "scale=4 ; ${EXPR}" | bc ) HUMAN=$(echo "${ANSWER}" | ${FORMAT} ) printf "%s ( %s )\n" ${ANSWER} ${HUMAN} done
__________________
You don't need to be a genius to debug a pf.conf firewall ruleset, you just need the guts to run tcpdump |
Tags |
arithmetic, bc(1), calculate, calculator |
Thread Tools | |
Display Modes | |
|
|
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
A simple question | Mr-Biscuit | Off-Topic | 1 | 16th April 2009 04:26 PM |
Simple Firewall with PF | jones | FreeBSD General | 3 | 7th November 2008 02:02 AM |
Help With [seemingly] Simple Problem | MustLearn | FreeBSD General | 3 | 7th October 2008 10:05 AM |
Simple/easy ircd | Weaseal | FreeBSD Ports and Packages | 0 | 17th July 2008 12:31 PM |
RPN Calculator?? | DrJ | FreeBSD General | 7 | 30th May 2008 01:16 AM |