DaemonForums  

Go Back   DaemonForums > Miscellaneous > Programming

Programming C, bash, Python, Perl, PHP, Java, you name it.

Reply
 
Thread Tools Display Modes
  #1   (View Single Post)  
Old 26th July 2019
J65nko J65nko is offline
Administrator
 
Join Date: May 2008
Location: Budel - the Netherlands
Posts: 3,567
Default sh script to convert inches to mm and cm

I found a few interesting DIY wood working projects on the interwebs and because the measurements were in inches I wrote a simple sh script to convert these to millimeters and centimeters.

Code:
#!/bin/sh

# use 'bc(1)', the multiprecision calculator to convert inches to mm and cm

INCH='25.4'     # millimeter (mm)
PRECISION=4     # nr of fractional digits

VALUE=1

cat <<END
You can enter values like '1 1/4' or '2 3/8' inch as '1+1/4' and '2+3/8'
-----------------------
Nr of inches: 1/8
1/8 inch = 3.1750 mm or .3175 cm

Nr of inches: 1+1/4
1+1/4 inch = 31.7500 mm or 3.1750 cm
-----------------------

Press CNTRL-C to exit.

END

while true ; do    
        printf "\nNr of inches: " 
        read VALUE
        MM=$( echo "scale = ${PRECISION} ; ( ${VALUE} ) * ${INCH}" | bc )
        CM=$( echo "scale = ${PRECISION} ; ${MM} / 10" | bc )
        echo "${VALUE} inch = ${MM} mm or ${CM} cm"
done
Example where I convert the size of a 2 by 4" and the 1/16" holes to drill in a 3/4" PVC irrigation pipe:

Code:
$ ./inch2milli.sh
You can enter values like '1 1/4' or '2 3/8' inch as '1+1/4' and '2+3/8'
-----------------------
Nr of inches: 1/8
1/8 inch = 3.1750 mm or .3175 cm

Nr of inches: 1+1/4
1+1/4 inch = 31.7500 mm or 3.1750 cm
-----------------------

Press CNTRL-C to exit.


Nr of inches: 2
2 inch = 50.8 mm or 5.0800 cm

Nr of inches: 4
4 inch = 101.6 mm or 10.1600 cm

Nr of inches: 1/16
1/16 inch = 1.5875 mm or .1587 cm

Nr of inches: 3/4
3/4 inch = 19.0500 mm or 1.9050 cm

Nr of inches: ^C$
Attached Files
File Type: sh inch2milli.sh (683 Bytes, 17 views)
__________________
You don't need to be a genius to debug a pf.conf firewall ruleset, you just need the guts to run tcpdump
Reply With Quote
  #2   (View Single Post)  
Old 26th July 2019
IdOp's Avatar
IdOp IdOp is offline
Too dumb for a smartphone
 
Join Date: May 2008
Location: twisting on the daemon's fork(2)
Posts: 882
Default

Thank you for posting that, I enjoy playing with shell scripts for odd purposes.

Unfortunately, my knowledge of bc(1) is about zero, so naturally the first thing that came to my mind after looking at your script was "Hmmm, could you do that just with shell arithmetic and without bc ?" At first I thought it would be problematic, but after thinking about it a bit it seemed it would not be that difficult. So I had some fun writing a script to do it (shown below).

Here is my "analysis" which also explains how the script works. The script takes 3 arguments,

$1 = A = whole number of inches

$2 = B = numerator of fractional part

$3 = C = denominator of fractional part

So, "A B C" means A+B/C inches, which will be converted to cm with 4 digits precision, as an example. A and B must be non-negative integers, and C a positive integer. Then

( A + B/C ) inch = N * 10^{-4} cm

where N is a non-negative integer that we seek to find. A little algebra gives

[ ( AC + B) / C ] * 10^4 inch = N cm

Using inch = 2.54 cm this gives an answer for N:

N = [ ( AC + B ) / C ] * 10^2 * 254 = [ ( AC + B ) * 25400 ] / C

But, shell arithmetic is going to truncate this division, so to round it I think we should add C/2 to the top. I.e., if X is the [...] then replace it wtih X+C/2 = (2X+C)/2. Then the rounded value for N is

N = [ ( AC + B ) * 25400 * 2 + C ] / (2C)

Finally we want the integer and fractional parts of N * 10^{-4}. These are:

I = floor( N / 10^4 )

and (remainder times 10^4):

R = ( N - I * 10*4 )

The answer is basically "I.R" printed properly with printf().

Code:
#!/bin/sh

# Inches to CentiMeters (to 4 decimal precision)
# Arguments:  $1 = whole inches;  $2 = fractional numerator;  $3 = fractional denominator
#             i.e.,  $1 + $2 / $3 [inches]
#             Both $2 and $3 can be omitted together (for a whole inch)

# Argument checking is a bit rudimentary ...

if [ $# -eq 0  -o  $# -eq 2  -o  $# -ge 4 ]; then
    echo Error a.
    exit
fi

A=$1
B=${2:-0}
C=${3:-1}

if [ $C -le 0 ]; then
    echo Error b.
    exit
fi

N=$(( ( A * C + B ) * 25400 * 2 + C ))

N=$(( N / ( 2 * C ) ))

I=$(( N / 10000 ))

R=$((  N - I * 10000 ))

printf ">>  %d + %d / %d in  =  %d.%04d cm\n" $A $B $C $I $R
(To convert a whole number of inches, the script allows a single argument.)

Comments and corrections are welcome, as I haven't checked this extensively, but it seems to work in several cases I tried.

EDIT: There's a bug in the attached file. The printf statement should contain %04d as shown in the code block. Ooops. Hmmm, it seems the file didn't attach, just as well I guess! You can copy from the code block.

Last edited by IdOp; 26th July 2019 at 07:56 PM.
Reply With Quote
  #3   (View Single Post)  
Old 2nd August 2019
J65nko J65nko is offline
Administrator
 
Join Date: May 2008
Location: Budel - the Netherlands
Posts: 3,567
Default

Interesting, but rather complicated ;-).

bc(1) uses a simple C-like language for arithmetic expressions. After parsing/pre-processing then feeds it to dc(1), a reverse-Polish notation unlimited precision utility.

I find it rather easy to use.
Another way is to use it from the command line like:

Code:
$ bc -e 'scale=4 ; (1/8) * 25.4' -e 'quit'
3.1750

$ echo 'scale=4 ; (1+1/4) * 25.4' | bc
31.7500
My script uses the last method.
__________________
You don't need to be a genius to debug a pf.conf firewall ruleset, you just need the guts to run tcpdump
Reply With Quote
  #4   (View Single Post)  
Old 3rd August 2019
IdOp's Avatar
IdOp IdOp is offline
Too dumb for a smartphone
 
Join Date: May 2008
Location: twisting on the daemon's fork(2)
Posts: 882
Default

bc and dc are certainly practical and powerful tools. They can do things that would go far beyond what is realistically feasible with shell arithmetic. Or for simpler tasks, like inches --> cm, they can be used to get results quickly (assuming one knows how to use them). For a project like yours, that's important, after all I'm sure what matters most is cutting or drilling the wood at the right size, and not getting lost in arcane arithmetic.

I vaguely recalled there were such calculation tools in Unix systems --- I think they've even been mentioned on daemonforums before --- but couldn't have remembered their names if asked. So this thread is a helpful reminder about them for one day when I might need to use them.

The shell approach just seemed like a natural question in the nature of minimalism, and for me at least is the kind of puzzle that's a lot of fun and hard to stop playing with once the question is posed.
Reply With Quote
  #5   (View Single Post)  
Old 3rd August 2019
ibara's Avatar
ibara ibara is offline
Real-life IT professor
 
Join Date: Jan 2014
Posts: 717
Default

Interestingly, these two scripts are not identical. They produce different outputs.
Try using the number 5/9 as your inches input.

IdOp's script will produce:
Code:
>>  0 + 5 / 9 in  =  1.4111 cm
whereas J65nko's script will produce:
Code:
5/9 inch = 14.1097 mm or 1.4109 cm
Wolfram|Alpha reports the answer is 1.4111:
https://www.wolframalpha.com/input/?...9+inches+in+cm

How do we reconcile this? This is because in J65nko's script, bc(1)'s scale factor is set to 4, which causes a premature truncation of the division. If you want bc(1) to output without the premature truncation, you should increase the scale factor beyond what you want for output (let's say 2x for safety), then use printf(1) to cap your output.

Something like this will indeed print the correct 1.4111:
Code:
$ echo 'scale=8 ; (5/9) * 2.54' | bc | xargs printf "%.4f\n"
Indeed, changing these three lines in J65nko's script from:
Code:
PRECISION=4     # nr of fractional digits
MM=$( echo "scale = ${PRECISION} ; ( ${VALUE} ) * ${INCH}" | bc )
CM=$( echo "scale = ${PRECISION} ; ${MM} / 10" | bc )
to:
Code:
PRECISION=8     # nr of fractional digits * 2
MM=$( echo "scale = ${PRECISION} ; ( ${VALUE} ) * ${INCH}" | bc | xargs printf "%.4f\n" )
CM=$( echo "scale = ${PRECISION} ; ${MM} / 10" | bc | xargs printf "%.4f\n" )
creates identical outputs.
Reply With Quote
  #6   (View Single Post)  
Old 3rd August 2019
IdOp's Avatar
IdOp IdOp is offline
Too dumb for a smartphone
 
Join Date: May 2008
Location: twisting on the daemon's fork(2)
Posts: 882
Default

That's interesting. It's conceivable that for any given safety factor, such as using 8 digits, there might be some narrow special case fraction inputs such that the rounded results are still different. (Just speculation on my part.) As for checking of my script's results, I just used a hand canculator, e.g.,

(5/9)* 2.54 = 1.41111111...

and observed it seemed to round to 4 digits properly in the few cases I checked.
Reply With Quote
  #7   (View Single Post)  
Old 8th August 2019
J65nko J65nko is offline
Administrator
 
Join Date: May 2008
Location: Budel - the Netherlands
Posts: 3,567
Default

Ibara, thanks for your remarks.

The rounding error is because the script evolved from using only whole non-fractional input to fractional input for drilling holes , like 1/4, 1/6, 1/8, 1/16.

In the metric system we have drill bit sizes ranging from 1, 1,5, 2, 3.5, 4, 4.5 mm and so on. When somebody recommends to use a 1/16" drill bit for a simple DIY drip irrigation system, I needed to see which of my metric drill bits is the best equivalent. Hence the 4 digit precision.
Code:
$ echo 'scale = 4; 25.4 / 16' | bc
1.5875
So here I would use a 1.5 mm drill bit and not a 2 mm which the normal rounding would tell me. At the end of the irrigation pipe you already have less pressure than at the beginning. So 1.5 mm would be the best choice.

Then I needed to convert PVC pipe diameters like 1-1/4". For my script to handle that I had to wrap the '1 + 1/4" inside parentheses to get the correct answer:
Code:
$  echo 'scale=4 ; 25.4 / 4 ' | bc
6.3500
 
$ echo 'scale=4 ; 6.35 + 25.4 ' | bc
31.75
 
# wrong!
$ echo 'scale=4; 1 + 1 / 4 * 25.4' | bc
7.3500 

# correct
$ echo 'scale=4; (1 + 1 / 4) * 25.4' | bc
31.7500
This is close to the 32 mm PVC irrigation pipe that I can get here.

To prevent the 5/9 rounding issue, a multiplication of 5 * 25.4 and then a division by 9 would be another method:
Code:
$ echo 'scale = 4 ; 5 * 25.4 / 9' | bc
14.1111
This, however, requires parsing of the input.
__________________
You don't need to be a genius to debug a pf.conf firewall ruleset, you just need the guts to run tcpdump
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
Problem with convert aleunix OpenBSD Packages and Ports 2 10th May 2012 01:52 PM
How to convert "no-nat" to new OpenBSD 4.7 NAT/RDR syntax J65nko Guides 0 3rd April 2011 12:14 AM
How to convert MD5 hash into shadow format? guitarscn General software and network 2 29th November 2010 12:54 PM
Want to convert my server to raid revzalot OpenBSD Installation and Upgrading 2 16th September 2009 07:56 PM
Convert varchar () to datetime datatype in MYSQL cksraj Programming 1 22nd June 2009 12:53 PM


All times are GMT. The time now is 02:08 AM.


Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd.
Content copyright © 2007-2010, the authors
Daemon image copyright ©1988, Marshall Kirk McKusick