diff --git a/django/forms/fields.py b/django/forms/fields.py
index 49f29db..581101a 100644
--- a/django/forms/fields.py
+++ b/django/forms/fields.py
@@ -18,10 +18,12 @@ except ImportError:
 from django.core import validators
 from django.core.exceptions import ValidationError
 from django.forms.util import ErrorList, from_current_timezone, to_current_timezone
-from django.forms.widgets import (TextInput, PasswordInput, HiddenInput,
+from django.forms.widgets import (
+    TextInput, IntegerInput, PasswordInput, EmailInput, URLInput, HiddenInput,
     MultipleHiddenInput, ClearableFileInput, CheckboxInput, Select,
     NullBooleanSelect, SelectMultiple, DateInput, DateTimeInput, TimeInput,
-    SplitDateTimeWidget, SplitHiddenDateTimeWidget, FILE_INPUT_CONTRADICTION)
+    SplitDateTimeWidget, SplitHiddenDateTimeWidget, FILE_INPUT_CONTRADICTION
+)
 from django.utils import formats
 from django.utils.encoding import smart_unicode, smart_str, force_unicode
 from django.utils.ipv6 import clean_ipv6_address
@@ -30,7 +32,6 @@ from django.utils.translation import ugettext_lazy as _
 # Provide this import for backwards compatibility.
 from django.core.validators import EMPTY_VALUES
 
-
 __all__ = (
     'Field', 'CharField', 'IntegerField',
     'DateField', 'TimeField', 'DateTimeField', 'TimeField',
@@ -198,12 +199,13 @@ class CharField(Field):
 
     def widget_attrs(self, widget):
         attrs = super(CharField, self).widget_attrs(widget)
-        if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
+        if self.max_length is not None and isinstance(widget, TextInput):
             # The HTML attribute is maxlength, not max_length.
             attrs.update({'maxlength': str(self.max_length)})
         return attrs
 
 class IntegerField(Field):
+    widget = IntegerInput
     default_error_messages = {
         'invalid': _(u'Enter a whole number.'),
         'max_value': _(u'Ensure this value is less than or equal to %(limit_value)s.'),
@@ -235,6 +237,14 @@ class IntegerField(Field):
             raise ValidationError(self.error_messages['invalid'])
         return value
 
+    def widget_attrs(self, widget):
+        attrs = super(IntegerField, self).widget_attrs(widget)
+        if self.min_value:
+            attrs['min'] = self.min_value
+        if self.max_value:
+            attrs['max'] = self.max_value
+        return attrs
+
 class FloatField(IntegerField):
     default_error_messages = {
         'invalid': _(u'Enter a number.'),
@@ -256,25 +266,22 @@ class FloatField(IntegerField):
             raise ValidationError(self.error_messages['invalid'])
         return value
 
-class DecimalField(Field):
+    def widget_attrs(self, widget):
+        attrs = super(FloatField, self).widget_attrs(widget)
+        attrs['step'] = 'any'
+        return attrs
+
+class DecimalField(IntegerField):
     default_error_messages = {
         'invalid': _(u'Enter a number.'),
-        'max_value': _(u'Ensure this value is less than or equal to %(limit_value)s.'),
-        'min_value': _(u'Ensure this value is greater than or equal to %(limit_value)s.'),
         'max_digits': _('Ensure that there are no more than %s digits in total.'),
         'max_decimal_places': _('Ensure that there are no more than %s decimal places.'),
         'max_whole_digits': _('Ensure that there are no more than %s digits before the decimal point.')
     }
 
     def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs):
-        self.max_value, self.min_value = max_value, min_value
         self.max_digits, self.decimal_places = max_digits, decimal_places
-        Field.__init__(self, *args, **kwargs)
-
-        if max_value is not None:
-            self.validators.append(validators.MaxValueValidator(max_value))
-        if min_value is not None:
-            self.validators.append(validators.MinValueValidator(min_value))
+        super(DecimalField, self).__init__(max_value, min_value, *args, **kwargs)
 
     def to_python(self, value):
         """
@@ -323,6 +330,14 @@ class DecimalField(Field):
             raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places))
         return value
 
+    def widget_attrs(self, widget):
+        attrs = super(DecimalField, self).widget_attrs(widget)
+        if self.max_digits:
+            attrs['maxlength'] = self.max_digits + 1 # for the dot
+        if self.decimal_places:
+            attrs['step'] = '0.%s1' % ('0' * (self.decimal_places-1))
+        return attrs
+
 class BaseTemporalField(Field):
 
     def __init__(self, input_formats=None, *args, **kwargs):
@@ -459,6 +474,7 @@ class RegexField(CharField):
     regex = property(_get_regex, _set_regex)
 
 class EmailField(CharField):
+    widget = EmailInput
     default_error_messages = {
         'invalid': _(u'Enter a valid e-mail address.'),
     }
@@ -585,6 +601,7 @@ class ImageField(FileField):
         return f
 
 class URLField(CharField):
+    widget = URLInput
     default_error_messages = {
         'invalid': _(u'Enter a valid URL.'),
     }
diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index 1fbef98..5ca7186 100644
--- a/django/forms/widgets.py
+++ b/django/forms/widgets.py
@@ -19,12 +19,11 @@ from django.utils.safestring import mark_safe
 from django.utils import datetime_safe, formats
 
 __all__ = (
-    'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'PasswordInput',
-    'HiddenInput', 'MultipleHiddenInput', 'ClearableFileInput',
-    'FileInput', 'DateInput', 'DateTimeInput', 'TimeInput', 'Textarea', 'CheckboxInput',
-    'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect',
-    'CheckboxSelectMultiple', 'MultiWidget',
-    'SplitDateTimeWidget',
+    'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'EmailInput', 'URLInput',
+    'IntegerInput', 'PasswordInput', 'HiddenInput', 'MultipleHiddenInput',
+    'ClearableFileInput', 'FileInput', 'DateInput', 'DateTimeInput', 'TimeInput',
+    'Textarea', 'CheckboxInput', 'Select', 'NullBooleanSelect', 'SelectMultiple',
+    'RadioSelect', 'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget',
 )
 
 MEDIA_TYPES = ('css','js')
@@ -242,6 +241,11 @@ class Input(Widget):
     """
     input_type = None # Subclasses must define this.
 
+    def __init__(self, attrs=None, input_type=None):
+        if input_type is not None:
+            self.input_type = input_type
+        super(Input, self).__init__(attrs)
+
     def _format_value(self, value):
         if self.is_localized:
             return formats.localize_input(value)
@@ -259,7 +263,16 @@ class Input(Widget):
 class TextInput(Input):
     input_type = 'text'
 
-class PasswordInput(Input):
+class IntegerInput(TextInput):
+    input_type = 'number'
+
+class EmailInput(TextInput):
+    input_type = 'email'
+
+class URLInput(TextInput):
+    input_type = 'url'
+
+class PasswordInput(TextInput):
     input_type = 'password'
 
     def __init__(self, attrs=None, render_value=False):
@@ -395,9 +408,7 @@ class Textarea(Widget):
         return mark_safe(u'<textarea%s>%s</textarea>' % (flatatt(final_attrs),
                 conditional_escape(force_unicode(value))))
 
-class DateInput(Input):
-    input_type = 'text'
-
+class DateInput(TextInput):
     def __init__(self, attrs=None, format=None):
         super(DateInput, self).__init__(attrs)
         if format:
@@ -426,9 +437,7 @@ class DateInput(Input):
             pass
         return super(DateInput, self)._has_changed(self._format_value(initial), data)
 
-class DateTimeInput(Input):
-    input_type = 'text'
-
+class DateTimeInput(TextInput):
     def __init__(self, attrs=None, format=None):
         super(DateTimeInput, self).__init__(attrs)
         if format:
@@ -457,9 +466,7 @@ class DateTimeInput(Input):
             pass
         return super(DateTimeInput, self)._has_changed(self._format_value(initial), data)
 
-class TimeInput(Input):
-    input_type = 'text'
-
+class TimeInput(TextInput):
     def __init__(self, attrs=None, format=None):
         super(TimeInput, self).__init__(attrs)
         if format:
