|The HotDocs Computation Archive|
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
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.
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