The HotDocs Computation Archive

# 0116 - (Even) Smarter Fraction Formatting

### Description:

Formats any decimal value as its fraction; omits the fraction portion for whole numbers.

### • Explanation •

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.

•  •  •  •  •  •  •

### • Code •

```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"
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
END IF

// Format the fraction
"«NumVar:9» «Numerator»/«Denominator»"
END IF
```

### • Explanation •

Required Elements:

• NumVar - Your number variable. Change NumVar in the computation to match the name of your number variable. Presumably it allows at least one decimal place (otherwise, why are you doing this?).
• Loop - A repeated dialog containing a single text variable, LoopLimit.
• LoopLimit - A text variable used to limit the loop. Set its Advanced options to "Don't warn if unanswered," "Ask only in dialog," and "Don't save in answer file."
• Numerator - A temporary number variable used to hold the numerator value. Set its Advanced options to "Don't warn if unanswered," "Ask only in dialog," and "Don't save in answer file."
• Denominator - A temporary number variable used to hold the denominator value. Set its Advanced options to "Don't warn if unanswered," "Ask only in dialog," and "Don't save in answer file."
• Temp-t - A temporary text variable. Set its Advanced options to "Don't warn if unanswered," "Ask only in dialog," and "Don't save in answer file."
• Temp-n - A temporary number variable. Set its Advanced options to "Don't warn if unanswered," "Ask only in dialog," and "Don't save in answer file."

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

•  •  •  •  •  •  •

### • Code •

```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
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
// 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 LoopResult-n TO UNANSWERED
END REPEAT

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
```

### • Explanation •

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")

• NumVar - Your number variable. Change NumVar in the computation to match the name of your number variable.
• Loop - A repeated dialog containing the following temporary variables:
• LoopLimit (text variable)
• LoopFraction-n
• LoopResult-n
• Temporary number variables:
• WholeNumber-n
• Numerator-n
• Denominator-n
• Test-n
• Temp-n
• Numerator
• Denominator
• Temporary true/false variables:
• FractionSet-b
• GreaterThanHalf-b
• (For all temporary variables, set the 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

### • Contributors •

 LegalCS Benjamin Reich, Esquire1103 Coventry AvenueCheltenham, PA 19012(215) 635-1705LawBase and HotDocs Consulting