Index: django/contrib/humanize/templatetags/humanize.py
===================================================================
--- django/contrib/humanize/templatetags/humanize.py	(revision 5357)
+++ django/contrib/humanize/templatetags/humanize.py	(working copy)
@@ -67,3 +67,101 @@
         return value
     return (_('one'), _('two'), _('three'), _('four'), _('five'), _('six'), _('seven'), _('eight'), _('nine'))[value-1]
 register.filter(apnumber)
+
+def _to_frac(x, maxdenom=10):
+    """
+    Convert x to a common fraction.
+    
+    Chooses the closest fraction to x with denominator <= maxdenom.
+    If x is closest to an integer, return that integer; otherwise,
+    return an (integer, numerator, denominator) tuple.
+    """
+    
+    assert x >= 0, "_to_frac only works on positive numbers."
+    
+    intpart = int(x)
+    x -= intpart
+    
+    bestfrac = 0,1
+    mindiff = x
+    
+    for denom in range(1,maxdenom+1):
+        # for each denominator, there are two numerators to consider:
+        # the one below x and the one above x
+        for num in (int(x*denom), int(x*denom+1)):
+            diff = abs(float(num)/denom - x)
+            
+            # compare using '<' rather than '<=' to ensure that the
+            # fraction with the smallest denominator is preferred
+            if diff < mindiff:
+                bestfrac = num, denom
+                mindiff = diff
+    
+    if bestfrac[0] == 0:
+        return intpart
+    elif mindiff >= 1-x:
+        return intpart+1
+    else:
+        return intpart, bestfrac[0], bestfrac[1]
+
+
+_frac_entities = {(1,4): "&frac14;", (1,2): "&frac12;", (3,4): "&frac34;",
+            (1, 3): "&#x2153;", (2, 3): "&#x2154;", (1, 5): "&#x2155;",
+            (2, 5): "&#x2156;", (3, 5): "&#x2157;", (4, 5): "&#x2158;",
+            (1, 6): "&#x2159;", (5, 6): "&#x215A;", (1, 8): "&#x215B;",
+            (3, 8): "&#x215C;", (5, 8): "&#x215D;", (7, 8): "&#x215E;"}
+
+def html_fraction (number, maxdenom=10, useUnicode=True):
+    """
+    Convert a float to a common fraction (or an integer if it is closer).
+    
+    If the output is a fraction, the fraction part is wrapped in a span
+    with class "fraction" to enable styling of fractions.
+    
+    If useUnicode is true, unicode entities will be used where available.
+    """
+    
+    number = float(number)
+    frac = _to_frac(abs(number), maxdenom)
+    
+    if type(frac) == int:
+        string = str(frac)
+    else:
+        intpart, numerator, denominator = frac
+        if useUnicode and (numerator, denominator) in _frac_entities:
+            fracpart = _frac_entities[(numerator, denominator)]
+        else:
+            fracpart = (('<span class="fraction"><sup>%i</sup>' + 
+                        '&#x2044;<sub>%i</sub></span>') %
+                        (numerator,denominator))
+        if intpart == 0:
+            string = fracpart
+        else:
+            string = str(intpart) + fracpart
+    
+    if number < 0:
+        return '-'+string
+    else:
+        return string
+register.filter(html_fraction)
+
+def text_fraction (number, maxdenom=10):
+    """Convert a float to a common fraction (or integer if it is closer)."""
+    
+    number = float(number)
+    frac = _to_frac(abs(number), maxdenom)
+    
+    if type(frac) == int:
+        string = str(frac)
+    else:
+        intpart, numerator, denominator = frac
+        if intpart == 0:
+            string = '%i/%i' % frac[1:]
+        else:
+            string = '%i %i/%i' % frac
+    
+    if number < 0:
+        return '-'+string
+    else:
+        return string
+register.filter(text_fraction)
Index: tests/regressiontests/humanize/tests.py
===================================================================
--- tests/regressiontests/humanize/tests.py	(revision 5357)
+++ tests/regressiontests/humanize/tests.py	(working copy)
@@ -48,6 +48,26 @@
                        'seven', 'eight', 'nine', '10')
 
         self.humanize_tester(test_list, result_list, 'apnumber')
+    
+    def test_html_fraction(self):
+        test_list = ['0', '0.5', '1.667', '-0.25', '-1.75', '9.99', '-3.2857']
+        result_list = ['0', '&frac12;', '1&#x2154;',
+                       '-&frac14;', '-1&frac34;', '10',
+                       '-3<span class="fraction"><sup>2</sup>&#x2044;<sub>7</sub></span>']
+                       
+        test_list = test_list + [float(num) for num in test_list]
+        result_list *= 2
+        
+        self.humanize_tester(test_list, result_list, 'html_fraction')
+    
+    def test_text_fraction(self):
+        test_list = ['0', '0.5', '1.667', '-0.25', '-1.75', '9.99']
+        result_list = ['0', '1/2', '1 2/3', '-1/4', '-1 3/4', '10']
+        
+        test_list = test_list + [float(num) for num in test_list]
+        result_list *= 2
+        
+        self.humanize_tester(test_list, result_list, 'text_fraction')
 
 if __name__ == '__main__':
     unittest.main()
Index: AUTHORS
===================================================================
--- AUTHORS	(revision 5357)
+++ AUTHORS	(working copy)
@@ -188,6 +188,7 @@
     plisk
     Daniel Poelzleithner <http://poelzi.org/>
     polpak@yahoo.com
+    Max Rabkin <max.rabkin@gmail.com>
     J. Rademaker
     Michael Radziej <mir@noris.de>
     Ramiro Morales <rm0@gmx.net>
Index: docs/add_ons.txt
===================================================================
--- docs/add_ons.txt	(revision 5357)
+++ docs/add_ons.txt	(working copy)
@@ -135,6 +135,25 @@
 
 You can pass in either an integer or a string representation of an integer.
 
+text_fraction
+-------------
+
+Converts a decimal fraction to a common fraction.
+
+Examples:
+
+    * ``0.5`` becomes ``'1/2'``
+    * ``9.99`` becomes ``'10'``
+    * ``-1.25`` becomes ``'-1 1/4'``
+
+You can pass in either an integer or a string representation of an integer.
+
+html_fraction
+-------------
+
+Like text_fraction, but generates HTML so the fraction is rendered nicely. The
+fractional part of the output is wrapped in ``<span class="fraction">`` tags.
+
 flatpages
 =========
 
