Problem. HotDocs' "9 1/8" fraction formatting is limited to eighths (and multiples thereof). Thus it forces all decimal values into one of the following fractions: 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8. There is not a format that accomodates anything but eighths. Further, HotDocs will include "0/8" if your number is whole.
Solution. These computations will format any decimal value as its representative fraction. So .1 becomes 1/10, .67 becomes 2/3, and .09 becomes 9/100. Your number is then returned as a text string formatted as "1 2/3". If there is no decimal value, the fraction will be omitted.
Both computations will work as-is. They look daunting, but the work has been done for you. As long as you create the required elements, you can simply copy and paste the computations.
The computations require the looping mechanism described in Computation #0015: Loops via REPEAT. Please refer to that page for some caveats about this method.
Differences. The first computation is the original "Smarter Fraction Formatter." It will work up to two decimal places. Its highest precision is 1/100. It is included here as a fairly accessible model of how this type of computation is performed.
The second computation, the "(Even) Smarter Fraction Formatter," uses a much more sophisticated algorithm to produce the highest precision available in HotDocs: 1/1000000000 (one one-billionth). Note that it is no harder to use than the first. Just copy and paste.
See also, Computation #0051: Format a Number as "nine point seven five", #0052: Format a Fraction as "four and one half", #0062: "All (100%)", #0150: Decimal Formatter.
• • • • • • •
Original Computation:
""
// Round to two decimal places
SET Temp-t TO "«ROUND( NumVar, 2 ):9.00»"
// Numerator is the decimal value
SET Numerator TO INTEGER( LAST( Temp-t, 2 ) )
// No decimal part. Just return their number.
IF Numerator = 0
"«NumVar:9»"
// There is a decimal. Find the fraction.
ELSE
// Special handling: 1/3
IF Numerator = 33
SET Numerator TO 1
SET Denominator TO 3
// Special handling: 2/3
ELSE IF Numerator = 66 OR Numerator = 67
SET Numerator TO 2
SET Denominator TO 3
// Find the fraction
ELSE
// Denominator is 100
SET Denominator TO 100
// Set up a loop
SET LoopLimit[ 100 ] TO "x"
ASK NONE
REPEAT Loop
// Count backward from 100, looking for greatest common multiples
SET Temp-n TO 101 - COUNTER
// Is this a common multiple?
IF REMAINDER( Numerator , Temp-n ) = 0
AND REMAINDER( Denominator , Temp-n ) = 0
// Yes! Divide both by the multiple
SET Numerator TO Numerator / Temp-n
SET Denominator TO Denominator / Temp-n
END IF
END REPEAT
// Clean up the loop
REPEAT Loop
SET LoopLimit TO UNANSWERED
END REPEAT
ASK DEFAULT
END IF
// Format the fraction
"«NumVar:9» «Numerator»/«Denominator»"
END IF
Required Elements:
The Nitty-Gritty. The computation first rounds your number to two decimal places and puts this value into a temporary text variable, Temp-t. This is necessary to extract the decimal portion. We can grab the decimal portion by just taking the LAST two characters of Temp-t and converting them to an integer. This will now be our numerator (e.g. .25 = 25/100).
At this point there is no need to continue if there is no numerator, so if that is the case the computation will simply return the integer portion of NumVar. But if there is a numerator, we should examine it to see if we have either 1/3 or 2/3, since these cannot be produced by our loop (they would end up as 33/100 and 33/50, respectively). If the numerator is 33, we can just change it to 1 and make the denominator 3 and be done. Or if the numerator is 66 or 67 (in the event of rounding), we make the numerator 2 and the denominator 3.
Failing the previous tests, we now are forced to come up with the fraction on our own. The starting value of the fraction is n/100. This will almost always need to be reduced (e.g. 50/100 can be reduced to 1/2). We do this by counting down backwards from 100, trying to find the greatest common multiples for the numerator and the denominator. Each time we find a common multiple (i.e. there is no remainder when they are divided by the number) we perform the division to reduce the numbers. By the time our count has reached 1, we have effectively reduced our original fraction.
Finally, we return the formatted number in the form 1 2/3. You can tweak the formatting to your liking.
Contributor: LegalCS
• • • • • • •
High-Precision Computation:
""
SET Numerator-n TO NumVar - TRUNCATE(NumVar, 0 )
SET WholeNumber-n TO TRUNCATE(NumVar, 0 )
// No decimal part. Just return the number.
IF Numerator-n = 0
"«NumVar:9»"
// There is a decimal. Find the fraction.
ELSE
// Initialize vars
SET Test-n TO Numerator-n
SET FractionSet-b TO FALSE
// Check to see if value > 1/2 (algorithm only works below 1/2)
// If value > 1/2, find result for (1 - value)
IF Test-n > 0.5
SET GreaterThanHalf-b TO TRUE
SET Test-n TO 1 - Test-n
ELSE
SET GreaterThanHalf-b TO FALSE
END IF
// Set up a loop
SET LoopLimit[ 10 ] TO "x"
SET Temp-n TO Test-n
ASK NONE
REPEAT Loop
// Run 10 iterations, looking for least
// common denominator
IF FractionSet-b = FALSE
SET Denominator-n TO ROUND((1 / Temp-n), 8)
// Is this a common denominator?
// If NumVar has 10 digit precision (i.e., database variable
// or combined HotDocs vars entered by user), use this test
IF ABSOLUTE VALUE(Denominator-n - ROUND(Denominator-n, 0)) / (MIN(Denominator-n, 100)) < 0.00125
// If NumVar is limited to 5 digit precision (i.e., standard
// single HotDocs var entered by user), use this test
// IF ABSOLUTE VALUE(Denominator-n - ROUND(Denominator-n, 0)) / (MIN(Denominator-n, 50)) < 0.0075
// Success! Set condition as true
SET FractionSet-b TO TRUE
SET LoopAnswered-n TO 1
SET LoopFraction-n TO 1
ELSE
// Failure, prepare for next iteration
SET Temp-n TO Denominator-n - TRUNCATE(Denominator-n, 0)
IF Temp-n > 0.5
SET Temp-n TO 1 - Temp-n
END IF
SET LoopFraction-n TO Temp-n
SET LoopAnswered-n TO 1
END IF
ELSE
SET LoopResult-n TO 1
END IF
END REPEAT
// Now do the loop in reverse
REPEAT Loop
SET Counter-n TO 11 - COUNTER
SET Answered-n TO LoopAnswered-n[ Counter-n ]
IF ANSWERED( Answered-n )
// Pull result from previous iteration
SET Counter-n TO Counter-n + 1
IF Counter-n = 11
SET Temp-n TO 1
ELSE
SET Temp-n TO LoopResult-n[ Counter-n ]
END IF
SET Counter-n TO Counter-n - 1
// Divide by the fraction in this iteration
SET Temp-n TO ROUND( Temp-n / LoopFraction-n[ Counter-n ], 0 )
// Set result for this iteration
SET LoopResult-n[ Counter-n ] TO Temp-n
END IF
END REPEAT
// Set numerator and denominator
SET Numerator TO ROUND(LoopResult-n[ 1 ], 0)
SET Denominator TO ROUND(Numerator / Test-n, 0)
// Clean up the loop
REPEAT Loop
SET LoopLimit TO UNANSWERED
SET LoopFraction-n TO UNANSWERED
SET LoopAnswered-n TO UNANSWERED
SET LoopResult-n TO UNANSWERED
END REPEAT
ASK DEFAULT
IF FractionSet-b = FALSE
//Did not work, set to "nnn/1000"
SET Test-n TO Test-n * 1000
SET Numerator TO ROUND(Test-n, 0)
SET Denominator TO 1000
// See if numerator and denominator are both
// divisible by prime factors of 1000, 2*2*2*5*5*5
IF REMAINDER(Numerator, 2) = 0
AND REMAINDER(Denominator, 2) = 0
SET Numerator TO Numerator / 2
SET Denominator TO Denominator / 2
END IF
IF REMAINDER(Numerator, 2) = 0
AND REMAINDER(Denominator, 2) = 0
SET Numerator TO Numerator / 2
SET Denominator TO Denominator / 2
END IF
IF REMAINDER(Numerator, 2) = 0
AND REMAINDER(Denominator, 2) = 0
SET Numerator TO Numerator / 2
SET Denominator TO Denominator / 2
END IF
IF REMAINDER(Numerator, 5) = 0
AND REMAINDER(Denominator, 5) = 0
SET Numerator TO Numerator / 5
SET Denominator TO Denominator / 5
END IF
IF REMAINDER(Numerator, 5) = 0
AND REMAINDER(Denominator, 5) = 0
SET Numerator TO Numerator / 5
SET Denominator TO Denominator / 5
END IF
IF REMAINDER(Numerator, 5) = 0
AND REMAINDER(Denominator, 5) = 0
SET Numerator TO Numerator / 5
SET Denominator TO Denominator / 5
END IF
END IF
//If value was > .5, invert numerator result
IF GreaterThanHalf-b
SET Numerator TO Denominator - Numerator
END IF
// Format the fraction
"«WholeNumber-n» «Numerator:9999»/«Denominator:9999»"
END IF
Required Elements: (Note: All variables but NumVar are temporary variables. Set their Advanced options to "Ask only in dialog," "Don't warn if unanswered," and "Don't save in answer file")
This "(Even) Smarter Fraction" computation will convert a decimal to a fraction for any numerator and denominator, denominator up to the limit of the precision of the HotDocs variable being tested. If the tested variable has ten digit precision (the HotDocs maximum precision), both the numerator and denominator can (theoretically) be between 1 and 10,000,000,000. However, even with 10 digit precision, results get unpredictable below 1/100000 and above 99999/100000.
If the computation is unable to come up with a decimal using its algorithm, it will convert the decimal to "nnn/1000", and then reduce by any applicable prime factors of both nnn and 1000.
Thanks to the Computation Archive for the original "Smarter Fraction" computation and for the structure used in this computation.
A Note on 1/3 and 2/3. This computation automatically recognizes 1/3 and 2/3, but only at three decimal places. Note the fractions produced by each of the following values:
.300 -> 3/10 .330 -> 33/100 .333 -> 1/3 .600 -> 3/5 .660 -> 33/50 .666 -> 2/3
If your variable is limited to two decimal places, you will need to do a special check for .33 and .66. The technique is demonstrated in the original "Smarter Fraction" computation above.
Contributor: Benjamin Reich, Esquire