Index: fields.py
===================================================================
--- fields.py	(revision 5061)
+++ fields.py	(working copy)
@@ -11,7 +11,7 @@
 import time
 
 __all__ = (
-    'Field', 'CharField', 'IntegerField',
+    'Field', 'CharField', 'IntegerField', 'FloatField',
     'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
     'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
     'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
@@ -132,6 +132,50 @@
             raise ValidationError(gettext(u'Ensure this value is greater than or equal to %s.') % self.min_value)
         return value
 
+class FloatField(Field):
+    def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs):
+        self.max_value, self.min_value, self.max_digits, self.decimal_places = max_value, min_value, max_digits, decimal_places
+        super(FloatField, self).__init__(*args, **kwargs)
+
+    def clean(self, value):
+        """
+        Validates that float() can be called on the input. Returns the result
+        of float(). Returns None for empty values.
+        """
+        super(FloatField, self).clean(value)
+        if value in EMPTY_VALUES:
+            return None
+        value = str(value).replace(',', '.')  # "," is used as fractional part separator in some languages (Russian, etc.)
+        value = value.strip()  # remove useless spaces
+        value = re.sub('^-\s+', '-', value)
+        value = re.sub('\s*\.\s*', '.', value)
+        try:
+            value = float(value)
+        except (ValueError, TypeError):
+            raise ValidationError(gettext(u'Enter a number.'))
+        if self.max_value is not None and value > self.max_value:
+            raise ValidationError(gettext(u'Ensure this value is less than or equal to %s.') % self.max_value)
+        if self.min_value is not None and value < self.min_value:
+            raise ValidationError(gettext(u'Ensure this value is greater than or equal to %s.') % self.min_value)
+        if self.decimal_places is not None:
+            regex = (r'\.\d{1,%d}$' % self.decimal_places) if self.decimal_places > 0 else r'\.0$'
+            regex = re.compile(regex)
+            if not regex.search(str(value)):
+                raise ValidationError(gettext(u"Ensure this value's fractional part has at most %d digits") % self.decimal_places)
+        if self.max_digits is not None and self.decimal_places is not None:
+            regex = r'^[-]?\d{1,%d}\.' % (self.max_digits - self.decimal_places)
+            regex = re.compile(regex)
+            if not regex.search(str(value)):
+                raise ValidationError(gettext(u"Ensure this value's sharp part has at most %d digits") % (self.max_digits - self.decimal_places))
+        elif self.max_digits is not None:
+            regex = r'^[-]?\d{1,%d}$' % self.max_digits
+            regex = re.compile(regex)
+            value_sams_trailing_zero = re.sub('\.(0$)?', '', str(value))
+            if not regex.search(value_sams_trailing_zero):
+                raise ValidationError(gettext(u'Ensure this value has at most %d digits (including sharp part)') % self.max_digits)
+        return value
+
+
 DEFAULT_DATE_INPUT_FORMATS = (
     '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
     '%b %d %Y', '%b %d, %Y',            # 'Oct 25 2006', 'Oct 25, 2006'
Index: test_floatfield.py
===================================================================
--- test_floatfield.py	(revision 0)
+++ test_floatfield.py	(revision 0)
@@ -0,0 +1,67 @@
+import django.newforms as forms
+from django.newforms.util import ValidationError
+from copy import copy
+import unittest
+
+class TestFloatFieldValidation(unittest.TestCase):
+    
+    def mutate_data(self, data):
+        for row in copy(data):
+            if row[0].startswith('0.'):
+                data.append((row[0][1:], row[1], row[2]))
+        for row in copy(data):
+            if not row[0].startswith('-'):
+                data.append(('-' + row[0], row[1], row[2]))
+        for row in copy(data):
+            if 0 > row[0].find('.'):
+                data.append((row[0] + '.0', row[1], row[2]))
+
+    def mutate_data_dots(self, data):
+        for row in copy(data):
+            if 0 <= row[0].find('.'):
+                data.append((row[0].replace('.', ','), row[1], row[2]))
+
+    def setUp(self):
+        self.valid_data = [
+            ('000',   None, None),
+            ('100',   None, None),
+            (' 100 ',   None, None),
+            ('0.364', None, None),
+        
+            ('100',   3, None),
+            ('0.364', 4, None),
+        
+            ('100',   None, 0),
+            ('0.364', None, 3),
+        
+            ('100',   3, 0),
+            ('0.364', 4, 3),
+            ]
+
+        self.invalid_data = [
+            ('sda',     None, None),
+            ('1,000.0', None, None),
+            ('--10',    None, None),
+            ('10-20',   None, None),
+            ]
+
+        self.mutate_data(self.valid_data)
+        self.mutate_data_dots(self.valid_data)
+        self.mutate_data(self.invalid_data)
+
+    def test_valid_values_passes(self):
+        for text, max_digits, decimal_places in self.valid_data:
+            field = forms.FloatField(max_digits = max_digits, decimal_places = decimal_places)
+            try:
+                field.clean(text)
+            except ValidationError, e:
+                self.fail('%s : %s' % (text, e))
+
+    def test_invalid_values_excepts(self):
+        for text, max_digits, decimal_places in self.invalid_data:
+            field = forms.FloatField(max_digits = max_digits, decimal_places = decimal_places)
+            self.assertRaises(ValidationError, field.clean, text)
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestFloatFieldValidation)
+unittest.TextTestRunner(verbosity=2).run(suite)
+
