Feet And Inches
July 1, 2011
Drawing programs measure distances in ten-thousandths of an inch, like 73.0185, but carpenters work in feet, inches, and thirty-seconds of an inch, like 6 feet 1 and 1/32 inches.
Your task is to write a program that takes a measurement in decimal notation and returns the measurement in carpenter’s notation; readers unfamiliar with imperial measurements will want to know that there are twelve inches in a foot. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.
My Haskell solution (see http://bonsaicode.wordpress.com/2011/07/01/programming-praxis-feet-and-inches/ for a version with comments):
import Data.Ratio import Text.Printf toCarpenter :: RealFrac a => a -> (Int, Int, Ratio Int) toCarpenter l = (feet, div r 32, mod r 32 % 32) where (feet, r) = divMod (round $ l * 32) (32 * 12) feetAndInches :: RealFrac a => a -> String feetAndInches l = case toCarpenter l of (0,0,0) -> "0 feet 0 inches" (f,i,t) -> showUnit "foot" "feet" (f % 1) ++ (if f > 0 && (i%1 + t) > 0 then " " else "") ++ showUnit "inch" "inches" (i % 1 + t) where showUnit _ _ 0 = "" showUnit s m n = printf "%s %s" (showVal n) $ if n <= 1 then s else m showVal v | d == 1 = show n | v < 1 = printf "%d/%d" n d | otherwise = printf "%d and %d/%d" (div n d) (mod n d) d where (n,d) = (numerator v, denominator v)[…] today’s Programming Praxis exercise, our goal is to convert a decimal length value to the fractions used by […]
Here’s a Python solution:
from math import floor from fractions import Fraction def convert(n): feet, r = int(floor(n) / 12), n % 12 inches = int(floor(r)) numer = int(round(32 * (r - inches))) return feet, inches, Fraction(numer, 32) # the rest is just pretty-printing def show_inches(inches, fractions): def pluralize_inches(i): if i == 1: return str(i) + " inch" else: return str(i) + " inches" if inches and fractions: return str(inches) + ' and ' + str(fractions) + ' inches' elif inches or fractions: return pluralize_inches(inches or fractions) else: return '' def feet_and_inches(n): def pluralize_feet(ft): if not ft: return '' elif ft == 1: return str(ft) + ' foot' else: return str(ft) + ' feet' if not n: return "0 feet 0 inches" else: feet, inches, fractions = convert(n) ft = pluralize_feet(feet) i = show_inches(inches, fractions) return ft + (ft and i and ' ') + iAlso here: http://paste.pocoo.org/show/426731/ and here: http://pastebin.com/McZHhzK0
My solution in Python:
#!/usr/bin/python3 import sys import fractions if __name__ == '__main__': if len(sys.argv) < 2: sys.stderr.write('Usage: {0} inches'.format(sys.argv[0])) sys.exit(2) inches = round(float(sys.argv[1]) * 32) / 32 feets = int(inches // 12) inches -= feets * 12 full = int(inches) inches -= full numerator = int(inches / (1 / 32)) if (feets, full, numerator) == (0, 0, 0): print('0 feet 0 inches') else: if feets != 0: print(feets, 'foot' if feets == 1 else 'feet', end = ' ') if full != 0: print(full, end = ' ') if numerator != 0 and full != 0: print('and', end = ' ') if numerator != 0: print(fractions.Fraction(numerator, 32), end = ' ') if numerator != 0 or full != 0: print('inches' if full > 1 or (full == 1 and numerator != 0) else 'inch', end = '') print()My try in REXX:
d_inch = 73.0185 say 'Drawing distance='d_inch '=' convert(d_inch) exit convert: parse arg inp parse value inp with vor'.'nach aus = '' ft = vor % 12 is = vor // 12 cz = format(32 / (10000 / nach),2,0) if ft > 0 then aus = ft 'feet' if is > 0 then aus = strip(aus) is if cz > 0 then if strip(aus) > '' then aus = strip(aus) 'and' aus = aus cz'/32' return strip(aus)from math import trunc from math import modf def carpenter_inches(inches): seconds, inches = modf(inches) feet, inches = divmod(trunc(inches), 12) seconds = trunc(seconds * 32) return feet, inches, seconds def string_measure(measure, singular, plural): if measure: if measure > 1: return "%d %s " % (measure, plural) else: return "1 %s " % (singular) return '' string_feet = lambda n: string_measure(n, 'foot', 'feet') string_inches = lambda n: string_measure(n, 'inch', 'inches') string_seconds = lambda n: string_measure(n, 'second', 'seconds') string_funcs = [string_feet, string_inches, string_seconds] def show_measure (measure): print ''.join([f(n) for f, n in zip(string_funcs, carpenter_inches(measure))])I just used ‘ and ” to indicate feet and inches. Until seeing other’s solutions it hadn’t occured to me to spell them out.
from math import ceil from fractions import Fraction def convert(m, ppi=32, roundup=False): units = int(ceil(m*ppi) if roundup else round(m*ppi)) feet, inch = divmod(units//ppi, 12) frac = Fraction(units%ppi, ppi) return ''.join([ "{0}'" if feet or not units else '', " {1}" if inch or not units else '', " {2}" if frac else '', '"' if inch or frac or not units else '' ]).lstrip().format(feet, inch, frac)Here’s my version modified to print out “feet” and “inches”.
from math import ceil from fractions import Fraction def convert(m, ppi=32, roundup=False): units = int(ceil(m*ppi) if roundup else round(m*ppi)) if not units: return "0 feet 0 inches" feet, inches = divmod(units, 12*ppi) whole, parts = divmod(inches, ppi) fmt = ''.join([ '{0} feet' if feet > 1 else '{0} foot' if feet else '' , ' {1}' if whole else '', ' and' if whole and parts else '', ' {2}' if parts else '', ' inches' if inches > ppi else ' inch' if inches else '' ]).lstrip() return fmt.format(feet, whole, Fraction(parts, ppi))Here’s one in C++
#include <iostream> using namespace std; void toCarpenter(double dec_inches) { if( !dec_inches ) { cout << "0 feet and 0 inches" << endl; return; } int inches = dec_inches; dec_inches -= inches; int feet = inches / 12; inches = inches % 12; int denominator = 32; int numerator = dec_inches * denominator; while( numerator % 2 == 0 ) { numerator /= 2; denominator /= 2; } if( feet ) { cout << feet; feet > 1 ? cout << " feet " : cout << " foot "; } if( inches )cout << inches; if( inches && numerator ) cout << " and "; if( numerator ) cout << numerator << "/" << denominator; inches > 1 ? cout << " inches" : cout << " inch"; } int main() { double inches = 26.375; toCarpenter( inches ); }namespace Carpenter
{
class Program
{
static void Caprenter(double measureInches, out int feet, out int inches, out int thirtyseconds)
{
feet = (int)(measureInches / 12);
inches = (int)(measureInches % 12);
thirtyseconds = (int)((measureInches – (feet * 12) – inches) * 32.0);
}
static void Main()
{
double measureInches = 26.375;
int feet, inches, thirtyseconds;
Caprenter(measureInches, out feet, out inches, out thirtyseconds);
System.Console.WriteLine(“Feet {0}, Inches {1}, 1/32 {2}”, feet, inches, thirtyseconds);
}
}
}
This is written in Excel VBA; it is called using: =CarpenterNotation(A1) for the defaults, or by selecting the varous optional parameters.
It can optionally round to various fractions, or 16th’s are the default; typical values would be 8 for 8ths or 16 for 16ths of an inch.
It can optionally round up or down, otherwise standard rounding occurs.
It can optionally display a dash on the left or right side for negative numbers, or parenthesis is the default.
It can optionally display a tilde to represent when the number is now an approximation due to rounding, or not display any notation, or a double-tilde is the default approximation notation.
Option Explicit Public Enum vbNegativeInd [_first] = -1 vbDashOnLeft = -1 'place dash on the left side for negative numbers vbParentheses = 0 'place parenthesis around negative numbers vbDashOnRight = 1 'place dash on the right side for negative numbers [_last] = 1 End Enum Public Enum vbRounding [_first] = -1 vbRoundDown = -1 'round down to the nearest fraction vbRound = 0 'standard rounding to the nearest fraction vbRoundUp = 1 'round up to the nearest fraction [_last] = 1 End Enum Public Enum vbApproxInd 'approximations are only used when rounding occurs [_first] = 0 vbNoIndicator = 0 'do not indicate approximations vbTilde = 1 'indicate approximations with a tilde on the left of the number vbDoubleTilde = 2 'indicate apprximatrions with a double-tilde on the left of the number [_last] = 2 End Enum Public Function CarpenterNotation(ByVal inches As Double, _ Optional ByVal smallestFractionSize As Long = 16, _ Optional ByVal negativeInd As vbNegativeInd = 0, _ Optional ByVal rounding As vbRounding = 0, _ Optional ByVal approxInd As vbApproxInd = 2) As String 'by Mark Main 'OUTPUT: feet, inches and the fraction with negative sign and approximation indicators optionally displayed 'inches: input is in inches with decimal up to 14 decimal places 'smallestFractionalSize: 16 for 1/16th inch increment, 8 for 1/8th, etc. 'negativeInd: (default is 0) equals -1 to place the negative dash on the left side, 0 for parentheses, and +1 for the dash on the right side 'rounding: (default is 0) equals 0 for standard rounding, 1 for round-up and -1 for round-down 'approxInd: (default is 2) equals 0 for no approximation indicator, 1 for a tilde ~, and 2 for a double-tilde ' double-tilde is also called: "almost equal to", "approximately equals" or "asymptotic to" symbol ' whenever rounding is required the display is then an approximation; the indicator will show this when it happens ' 'Note: blanks are provided to help positive numbers align with negative numbers; use Trim to bypass this feature: =Trim(CarpenterNotation(A1)) Dim feet, GCDnum, numerator, denominator As Long Dim originalFraction, fraction As Double Dim negative As Boolean Dim negind, leftparen, rightparen As String On Error GoTo CarpenterNotationErr If inches < 0 Then 'negative number negative = True inches = Abs(inches) End If 'round the fraction to the nearest fraction based upon smallestFractionalSize originalFraction = Round(inches - Int(inches), 14) 'rounding at 14 eliminates some unusual floating decimal errors from the Double data type Select Case rounding Case vbRoundDown: fraction = Application.WorksheetFunction.RoundDown(originalFraction * smallestFractionSize, 0) / smallestFractionSize Case vbRoundUp: fraction = Application.WorksheetFunction.RoundUp(originalFraction * smallestFractionSize, 0) / smallestFractionSize Case Else: fraction = Round(originalFraction * smallestFractionSize, 0) / smallestFractionSize End Select GCDnum = Application.WorksheetFunction.GCD(fraction * smallestFractionSize, smallestFractionSize) 'used to reduce fractions; e.g. 12/16 to 3/4 feet = Int(inches / 12) inches = Int(inches - (feet * 12)) numerator = fraction * 16 / GCDnum denominator = 16 / GCDnum CarpenterNotation = inches & "-" & numerator & "/" & denominator & """" If feet <> 0 Then CarpenterNotation = feet & "' " & CarpenterNotation 'feet is ignored if zero 'add negative indicators for negative numbers and blank spaces otherwise If negative Then negind = "-" leftparen = "(" rightparen = ")" Else negind = " " leftparen = " " rightparen = " " End If Select Case negativeInd Case 1: CarpenterNotation = CarpenterNotation & negind Case 0: CarpenterNotation = leftparen & CarpenterNotation & rightparen Case Else: CarpenterNotation = negind & CarpenterNotation End Select 'add the approximation indicator if needed If fraction <> originalFraction Then 'this is an approximation If approxInd = vbTilde Then CarpenterNotation = "~" & CarpenterNotation 'tilde added to indicate value is an approximation ElseIf approxInd <> vbNoIndicator Then 'default CarpenterNotation = ChrW(8776) & CarpenterNotation 'double-tilde added to indicate value is an approximation End If Else If approxInd <> vbNoIndicator Then CarpenterNotation = " " & CarpenterNotation 'add two blanks to cover for the tilde's End If On Error GoTo 0 Exit Function CarpenterNotationErr: On Error GoTo 0 CarpenterNotation = "#ERROR" End FunctionI made some changes. First I fixed a bug if zero appears in the denominator. I forgot to test for that, so I fixed it. And I made an option to decide if you want to display zero inches when there is feet; otherwise inches will always display if feet is zero.
Also, I decided to go with no approximation indication (FALSE value) or TRUE value to display approximation by using a tilde to show that the actual value is LESS than the rounded value displayed and I show the double tilde if the actual value is GREATER than what is displayed. This lets you know if the display is just a hair light or heavy simply by looking at the tilde’s.
So now TRUE/FALSE is used for the last 2 options:
Option Explicit Public Enum vbNegativeInd [_first] = -1 vbDashOnLeft = -1 'place dash on the left side for negative numbers vbParentheses = 0 'place parenthesis around negative numbers vbDashOnRight = 1 'place dash on the right side for negative numbers [_last] = 1 End Enum Public Enum vbRounding [_first] = -1 vbRoundDown = -1 'round down to the nearest fraction vbRound = 0 'standard rounding to the nearest fraction vbRoundUp = 1 'round up to the nearest fraction [_last] = 1 End Enum Public Function CarpenterNotation(ByVal inches As Double, _ Optional ByVal smallestFractionSize As Long = 16, _ Optional ByVal negativeInd As vbNegativeInd = 0, _ Optional ByVal rounding As vbRounding = 0, _ Optional ByVal dispZeroInches As Boolean = True, _ Optional ByVal approxIndication As Boolean = True) As String 'by Mark Main 'OUTPUT: feet, inches and the fraction with negative sign and approximation indicators optionally displayed 'inches: input is in inches with decimal up to 14 decimal places 'smallestFractionalSize: 16 for 1/16th inch increment, 8 for 1/8th, etc. 'negativeInd: (default is 0) equals -1 to place the negative dash on the left side, 0 for parentheses, and +1 for the dash on the right side 'rounding: (default is 0) equals 0 for standard rounding, 1 for round-up and -1 for round-down 'dispZeroInches: (default TRUE) equals TRUE when zero inches is displayed with the feet; if FALSE zero inches will not be display unless feet equals zero 'approxIndication: (default true) equals FALSE for no approximation indicator, or a ' TRUE shows a tilde or double-tilde when rounding causes the number to be smaller or larger respectively to the original number ' double-tilde is also called: "almost equal to", "approximately equals" or "asymptotic to" symbol ' whenever rounding is required the display is then an approximation; the indicator will show this when it happens ' 'Note: blanks are provided to help positive numbers align with negative numbers; use Trim to bypass this feature: =Trim(CarpenterNotation(A1)) Dim feet, GCDnum, numerator, denominator As Long Dim originalFraction, fraction As Double Dim negative As Boolean Dim negind, leftparen, rightparen As String On Error GoTo CarpenterNotationErr If inches < 0 Then 'negative number negative = True inches = Abs(inches) End If 'round the fraction to the nearest fraction based upon smallestFractionalSize originalFraction = Round(inches - Int(inches), 14) 'rounding at 14 eliminates some unusual floating decimal errors from the Double data type Select Case rounding Case vbRoundDown: fraction = Application.WorksheetFunction.RoundDown(originalFraction * smallestFractionSize, 0) / smallestFractionSize Case vbRoundUp: fraction = Application.WorksheetFunction.RoundUp(originalFraction * smallestFractionSize, 0) / smallestFractionSize Case Else: fraction = Round(originalFraction * smallestFractionSize, 0) / smallestFractionSize End Select GCDnum = Application.WorksheetFunction.GCD(fraction * smallestFractionSize, smallestFractionSize) 'used to reduce fractions; e.g. 12/16 to 3/4 feet = Int(inches / 12) inches = Int(inches - (feet * 12)) numerator = fraction * 16 / GCDnum denominator = 16 / GCDnum If numerator > 0 Then CarpenterNotation = inches & "-" & numerator & "/" & denominator & """" ElseIf dispZeroInches And feet > 0 Or feet = 0 Then CarpenterNotation = inches & """" 'zero inches is only displayed when necessary End If If feet <> 0 Then CarpenterNotation = feet & "' " & CarpenterNotation 'feet is ignored if zero 'add negative indicators for negative numbers and blank spaces otherwise If negative Then negind = "-" leftparen = "(" rightparen = ")" Else negind = " " leftparen = " " rightparen = " " End If Select Case negativeInd Case 1: CarpenterNotation = CarpenterNotation & negind Case 0: CarpenterNotation = leftparen & CarpenterNotation & rightparen Case Else: CarpenterNotation = negind & CarpenterNotation End Select 'add the approximation indicator if needed If approxIndication = True Then 'a tilde or double-tilde will be used when approximation occurs, otherwise blanks If fraction < originalFraction Then CarpenterNotation = "~" & CarpenterNotation 'tilde added to indicate value is an approximation; rounded value less than the original ElseIf fraction > originalFraction Then CarpenterNotation = ChrW(8776) & CarpenterNotation 'double-tilde added to indicate value is an approximation; rounded value is greater than original Else CarpenterNotation = " " & CarpenterNotation 'add two blanks to cover for the tilde's; zero rounding was needed End If End If On Error GoTo 0 Exit Function CarpenterNotationErr: On Error GoTo 0 CarpenterNotation = "#ERROR" End FunctionSorry, I wrote my notes backwards–I wish that I could edit it. For the tilde indication for my code above here is the rules:
if actual cell value > rounded display value then a tilde
if actual cell value < rounded display value then a double-tilde
saying the same thing in the reverse way:
if rounded display value actual cell value then a double-tilde
This is my final version if you’d like to have it–it’s public domain.
I added some error checking and found a bug if the value was just barely under 1 foot, 2 feet, etc, then it would round up but show 0/16″ Ooops, sorry. It’s fixed now.
I have tested this really well and it works solidly, I hope that you like it, sorry for the different versions, but the final product is nice.
Option Explicit Public Enum vbNegativeInd [_first] = -1 vbDashOnLeft = -1 'place dash on the left side for negative numbers vbParentheses = 0 'place parenthesis around negative numbers vbDashOnRight = 1 'place dash on the right side for negative numbers [_last] = 1 End Enum Public Enum vbRounding [_first] = -1 vbRoundDown = -1 'round down to the nearest fraction vbRound = 0 'standard rounding to the nearest fraction vbRoundUp = 1 'round up to the nearest fraction [_last] = 1 End Enum Public Function CarpenterNotation(ByVal inches As Double, _ Optional ByVal smallestFractionSize As Long = 16, _ Optional ByVal negativeInd As vbNegativeInd = 0, _ Optional ByVal rounding As vbRounding = 0, _ Optional ByVal dispZeroInches As Boolean = True, _ Optional ByVal approxIndication As Boolean = True) As String 'by Mark Main 'OUTPUT: feet, inches and the fraction with negative sign and approximation indicators optionally displayed 'inches: input is in inches with decimal up to 13 decimal places will work, but output rounding is limited to 10 decimals; ' this accuracy is beyond any physical machine measurements 'smallestFractionSize: 16 for 1/16th inch increment, 8 for 1/8th, etc.; the limit is 100,000, which is rediculously accurate 'negativeInd: (default is 0) equals -1 to place the negative dash on the left side, 0 for parentheses, and +1 for the dash on the right side 'rounding: (default is 0) equals 0 for standard rounding, 1 for round-up and -1 for round-down 'dispZeroInches: (default TRUE) equals TRUE when zero inches is displayed with the feet; if FALSE zero inches will not be display unless feet equals zero 'approxIndication: (default TRUE) equals FALSE for no approximation indicator, or a ' TRUE shows a tilde or double-tilde when rounding causes the number to be smaller or larger respectively to the original number ' double-tilde is also called: "almost equal to", "approximately equals" or "asymptotic to" symbol ' whenever rounding is required the display is then an approximation; the indicator will show this when it happens ' 'Note: blanks are provided to help positive numbers align with negative numbers; use Trim to bypass this feature: =Trim(CarpenterNotation(A1)) ' if you don't like the space between the tilde and the number then use a +1 for the negativeInd parameter Dim feet, GCDnum, numerator, denominator, numMetric As Long Dim originalInches, originalFraction, inchFraction, fraction, mmRound, mmOriginal, mmOriginalRound, mm As Double Dim negative As Boolean Dim negInd, leftParen, rightParen, Measurement, approxSign As String On Error GoTo CarpenterNotationErr If inches < 0 Then 'negative number negative = True inches = Abs(inches) End If 'round the fraction to the nearest fraction based upon smallestFractionSize originalInches = inches originalFraction = Round(inches - Int(inches), 14) 'rounding at 14 eliminates some unusual floating decimal errors from the Double data type If smallestFractionSize > 100000 Then smallestFractionSize = 100000 'there has to be some limit, this provides .00001" accuracy or 1/65536" (1/16^4") Select Case rounding Case vbRoundDown inchFraction = Application.WorksheetFunction.RoundDown(originalFraction * smallestFractionSize, 0) / smallestFractionSize 'round to nearest fraction Case vbRoundUp inchFraction = Application.WorksheetFunction.RoundUp(originalFraction * smallestFractionSize, 0) / smallestFractionSize 'round to nearest fraction Case Else inchFraction = Round(originalFraction * smallestFractionSize, 0) / smallestFractionSize 'round to nearest fraction End Select inches = Int(inches) + inchFraction inchFraction = inches - Int(inches) 'must be redone because inches is now rounded correctly GCDnum = Application.WorksheetFunction.GCD(inchFraction * smallestFractionSize, smallestFractionSize) 'used to reduce fractions; e.g. 12/16 to 3/4 numerator = inchFraction * smallestFractionSize / GCDnum denominator = smallestFractionSize / GCDnum feet = Int(inches / 12) inches = Int(inches Mod 12) 'inches in excess of the feet If numerator > 0 Then CarpenterNotation = inches & "-" & numerator & "/" & denominator & """" ElseIf dispZeroInches And feet > 0 Or feet = 0 Then CarpenterNotation = inches & """" 'zero inches is only displayed when necessary End If If feet <> 0 Then CarpenterNotation = feet & "' " & CarpenterNotation 'feet is ignored if zero 'add negative indicators for negative numbers and blank spaces otherwise If negative Then negInd = "-" leftParen = "(" rightParen = ")" Else negInd = " " leftParen = " " rightParen = " " End If Select Case negativeInd Case 1: CarpenterNotation = CarpenterNotation & negInd Case 0: CarpenterNotation = leftParen & CarpenterNotation & rightParen Case Else: CarpenterNotation = negInd & CarpenterNotation End Select 'add the approximation indicator if needed If approxIndication = True Then 'a tilde or double-tilde will be used when approximation occurs, otherwise blanks If Not negative And inchFraction < originalFraction Or negative And inchFraction > originalFraction Then CarpenterNotation = "~" & CarpenterNotation 'tilde added to indicate value is an approximation; rounded value less than the original ElseIf Not negative And inchFraction > originalFraction Or negative And inchFraction < originalFraction Then CarpenterNotation = ChrW(8776) & CarpenterNotation 'double-tilde added to indicate value is an approximation; rounded value is greater than original Else CarpenterNotation = " " & CarpenterNotation 'add two blanks to cover for the tilde's; zero rounding was needed End If End If On Error GoTo 0 Exit Function CarpenterNotationErr: On Error GoTo 0 CarpenterNotation = "#ERROR" End FunctionIn FORTH, still rounds down sometimes but mostly correct. Also I just print “inch(es)” as shortcut…
: even>0? ( n -- ? ) dup 1 and 0= swap 0> and ; : rounded ( n -- n ) 10 /mod swap 5 + 10 / + ; : .feet ( n -- ) ?dup IF dup . 1 = IF ." foot " ELSE ." feet " THEN THEN ; : .frac ( n/32 -- ) 32 swap BEGIN dup even>0? WHILE 2/ swap 2/ swap REPEAT 0 .r ." /" . ; : .inches ( frac n -- ) 2dup D0= IF 2drop ELSE ?dup 0= IF 32 1000 */ .frac ." inch" ELSE . 32 1000 */ ?dup IF ." and " .frac THEN ." inch(es)" THEN THEN ; : .carpenter ( d -- ) drop dup 0= IF ." 0 feet 0 inches" ELSE rounded 1000 /mod 12 /mod .feet .inches THEN ;Execution:
here’s a python solution i came up with.. precision can be changed in line 6&7… change the 16 to 4, 8, 32, 64 etc.. (it’s in 16ths right now as that’s typically what carpenters use)
import math import fractions def feet_inches(decimal): tol = round(decimal * 16) a = math.modf(tol / 16) feet = int(a[1] // 12) inch = int(a[1] % 12) fract = fractions.Fraction(a[0]) if fract == 0: fract = "" fi= str(feet)+" - "+str(inch)+" "+str(fract) return fi print feet_inches(88.3493475525)7 – 4 3/8