diff --git a/m3u8/model.py b/m3u8/model.py
index f9e90bf..01d9428 100644
--- a/m3u8/model.py
+++ b/m3u8/model.py
@@ -2,7 +2,7 @@
 # Copyright 2014 Globo.com Player authors. All rights reserved.
 # Use of this source code is governed by a MIT License
 # license that can be found in the LICENSE file.
-
+import decimal
 import os
 import errno
 import math
@@ -292,14 +292,14 @@ class M3U8(object):
             output.append('#EXT-X-MEDIA-SEQUENCE:' + str(self.media_sequence))
         if self.discontinuity_sequence:
             output.append('#EXT-X-DISCONTINUITY-SEQUENCE:{}'.format(
-                int_or_float_to_string(self.discontinuity_sequence)))
+                number_to_string(self.discontinuity_sequence)))
         if self.allow_cache:
             output.append('#EXT-X-ALLOW-CACHE:' + self.allow_cache.upper())
         if self.version:
             output.append('#EXT-X-VERSION:' + str(self.version))
         if self.target_duration:
             output.append('#EXT-X-TARGETDURATION:' +
-                          int_or_float_to_string(self.target_duration))
+                          number_to_string(self.target_duration))
         if not (self.playlist_type is None or self.playlist_type == ''):
             output.append('#EXT-X-PLAYLIST-TYPE:%s' % str(self.playlist_type).upper())
         if self.start:
@@ -503,7 +503,7 @@ class Segment(BasePathMixin):
 
         if self.uri:
             if self.duration is not None:
-                output.append('#EXTINF:%s,' % int_or_float_to_string(self.duration))
+                output.append('#EXTINF:%s,' % number_to_string(self.duration))
                 if self.title:
                     output.append(self.title)
                 output.append('\n')
@@ -627,7 +627,7 @@ class PartialSegment(BasePathMixin):
             output.append('#EXT-X-GAP\n')
 
         output.append('#EXT-X-PART:DURATION=%s,URI="%s"' % (
-            int_or_float_to_string(self.duration), self.uri
+            number_to_string(self.duration), self.uri
         ))
 
         if self.independent:
@@ -1039,9 +1039,9 @@ class RenditionReport(BasePathMixin):
     def dumps(self):
         report = []
         report.append('URI=' + quoted(self.uri))
-        report.append('LAST-MSN=' + int_or_float_to_string(self.last_msn))
+        report.append('LAST-MSN=' + number_to_string(self.last_msn))
         if self.last_part is not None:
-            report.append('LAST-PART=' + int_or_float_to_string(
+            report.append('LAST-PART=' + number_to_string(
                 self.last_part))
 
         return ('#EXT-X-RENDITION-REPORT:' + ','.join(report))
@@ -1074,7 +1074,7 @@ class ServerControl(object):
             if self[attr]:
                 ctrl.append('%s=%s' % (
                     denormalize_attribute(attr),
-                    int_or_float_to_string(self[attr])
+                    number_to_string(self[attr])
                 ))
 
         return '#EXT-X-SERVER-CONTROL:' + ','.join(ctrl)
@@ -1087,7 +1087,7 @@ class Skip(object):
         self.skipped_segments = skipped_segments
 
     def dumps(self):
-        return '#EXT-X-SKIP:SKIPPED-SEGMENTS=%s' % int_or_float_to_string(
+        return '#EXT-X-SKIP:SKIPPED-SEGMENTS=%s' % number_to_string(
             self.skipped_segments)
 
     def __str__(self):
@@ -1098,7 +1098,7 @@ class PartInformation(object):
         self.part_target = part_target
 
     def dumps(self):
-        return '#EXT-X-PART-INF:PART-TARGET=%s' % int_or_float_to_string(
+        return '#EXT-X-PART-INF:PART-TARGET=%s' % number_to_string(
             self.part_target)
 
     def __str__(self):
@@ -1124,7 +1124,7 @@ class PreloadHint(BasePathMixin):
             if self[attr] is not None:
                 hint.append('%s=%s' % (
                     denormalize_attribute(attr),
-                    int_or_float_to_string(self[attr])
+                    number_to_string(self[attr])
                 ))
 
         return ('#EXT-X-PRELOAD-HINT:' + ','.join(hint))
@@ -1187,9 +1187,9 @@ class DateRange(object):
         if (self.end_date):
             daterange.append('END-DATE=' + quoted(self.end_date))
         if (self.duration):
-            daterange.append('DURATION=' + int_or_float_to_string(self.duration))
+            daterange.append('DURATION=' + number_to_string(self.duration))
         if (self.planned_duration):
-            daterange.append('PLANNED-DURATION=' + int_or_float_to_string(self.planned_duration))
+            daterange.append('PLANNED-DURATION=' + number_to_string(self.planned_duration))
         if (self.scte35_cmd):
             daterange.append('SCTE35-CMD=' + self.scte35_cmd)
         if (self.scte35_out):
@@ -1231,5 +1231,8 @@ def quoted(string):
     return '"%s"' % string
 
 
-def int_or_float_to_string(number):
-    return str(int(number)) if number == math.floor(number) else str(number)
+def number_to_string(number):
+    with decimal.localcontext() as ctx:
+        ctx.prec = 20  # set floating point precision
+        d = decimal.Decimal(str(number))
+        return str(d.quantize(decimal.Decimal(1)) if d == d.to_integral_value() else d.normalize())

diff --git a/tests/playlists.py b/tests/playlists.py
index 38b6f38..edcce8b 100755
--- a/tests/playlists.py
+++ b/tests/playlists.py
@@ -25,6 +25,18 @@ http://media.example.com/entire2.ts
 #EXT-X-ENDLIST
 '''
 
+SIMPLE_PLAYLIST_WITH_VERY_SHORT_DURATION = '''
+#EXTM3U
+#EXT-X-TARGETDURATION:5220
+#EXTINF:5220,
+http://media.example.com/entire1.ts
+#EXTINF:5218.5,
+http://media.example.com/entire2.ts
+#EXTINF:0.000011,
+http://media.example.com/entire3.ts
+#EXT-X-ENDLIST
+'''
+
 SIMPLE_PLAYLIST_WITH_START_NEGATIVE_OFFSET = '''
 #EXTM3U
 #EXT-X-TARGETDURATION:5220
diff --git a/tests/test_model.py b/tests/test_model.py
index 3b6f2dd..323a418 100755
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -645,6 +645,14 @@ def test_dump_should_not_ignore_zero_duration():
     assert "EXTINF:5220" in obj.dumps().strip()
 
 
+def test_dump_should_use_decimal_floating_point_for_very_short_durations():
+    obj = m3u8.M3U8(playlists.SIMPLE_PLAYLIST_WITH_VERY_SHORT_DURATION)
+
+    assert "EXTINF:5220" in obj.dumps().strip()
+    assert "EXTINF:5218.5" in obj.dumps().strip()
+    assert "EXTINF:0.000011" in obj.dumps().strip()
+
+
 def test_dump_should_include_segment_level_program_date_time():
     obj = m3u8.M3U8(playlists.DISCONTINUITY_PLAYLIST_WITH_PROGRAM_DATE_TIME)
     # Tag being expected is in the segment level, not the global one

Short segment duration is formatted in scientific notation which violates the spec for EXTINF
Short segments are re-formatted in scientific notation(`e-05`) which violates the [decimal floating point spec](https://tools.ietf.org/html/rfc8216#section-4.3.2.1).

Using the attached file perform the following

```
>>> p = m3u8.load("index-source-test.m3u8.txt")
>>> o = p.dumps()
>>> f = open('/tmp/index-source-lib.m3u8', 'w')
>>> f.write(o)
```

```
cat /tmp/index-source-lib.m3u8

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:4
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:1.1e-05,
segment0.ts?flagset=source
#EXT-X-ENDLIST
```

You can see that `#EXTINF:0.000011` got reformatted into `#EXTINF:1.1e-05`
tests/test_model.py::test_dump_should_use_decimal_floating_point_for_very_short_durations
tests/test_model.py::test_target_duration_attribute tests/test_model.py::test_media_sequence_attribute tests/test_model.py::test_program_date_time_attribute tests/test_model.py::test_program_date_time_attribute_for_each_segment tests/test_model.py::test_program_date_time_attribute_with_discontinuity tests/test_model.py::test_program_date_time_attribute_without_discontinuity tests/test_model.py::test_segment_discontinuity_attribute tests/test_model.py::test_segment_cue_out_attribute tests/test_model.py::test_segment_cue_out_start_attribute tests/test_model.py::test_segment_cue_in_attribute tests/test_model.py::test_segment_cue_out_cont_dumps tests/test_model.py::test_segment_cue_out_start_dumps tests/test_model.py::test_segment_cue_out_start_no_duration_dumps tests/test_model.py::test_segment_cue_out_in_dumps tests/test_model.py::test_segment_elemental_scte35_attribute tests/test_model.py::test_segment_envivio_scte35_attribute tests/test_model.py::test_segment_unknown_scte35_attribute tests/test_model.py::test_segment_cue_out_no_duration tests/test_model.py::test_keys_on_clear_playlist tests/test_model.py::test_keys_on_simple_encrypted_playlist tests/test_model.py::test_key_attribute tests/test_model.py::test_key_attribute_on_none tests/test_model.py::test_key_attribute_without_initialization_vector tests/test_model.py::test_session_keys_on_clear_playlist tests/test_model.py::test_session_keys_on_simple_encrypted_playlist tests/test_model.py::test_session_key_attribute tests/test_model.py::test_session_key_attribute_on_none tests/test_model.py::test_session_key_attribute_without_initialization_vector tests/test_model.py::test_segments_attribute tests/test_model.py::test_segments_attribute_without_title tests/test_model.py::test_segments_attribute_without_duration tests/test_model.py::test_segments_attribute_with_byterange tests/test_model.py::test_segment_attribute_with_multiple_keys tests/test_model.py::test_segment_title_dumps tests/test_model.py::test_is_variant_attribute tests/test_model.py::test_is_endlist_attribute tests/test_model.py::test_is_i_frames_only_attribute tests/test_model.py::test_playlists_attribute tests/test_model.py::test_playlists_attribute_without_program_id tests/test_model.py::test_playlists_attribute_with_resolution tests/test_model.py::test_iframe_playlists_attribute tests/test_model.py::test_version_attribute tests/test_model.py::test_version_settable_as_int tests/test_model.py::test_version_settable_as_string tests/test_model.py::test_allow_cache_attribute tests/test_model.py::test_files_attribute_should_list_all_files_including_segments_and_key tests/test_model.py::test_vod_playlist_type_should_be_imported_as_a_simple_attribute tests/test_model.py::test_event_playlist_type_should_be_imported_as_a_simple_attribute tests/test_model.py::test_independent_segments_should_be_true tests/test_model.py::test_independent_segments_should_be_false tests/test_model.py::test_no_playlist_type_leaves_attribute_empty tests/test_model.py::test_dump_playlists_with_resolution tests/test_model.py::test_dump_should_build_file_with_same_content tests/test_model.py::test_dump_should_create_sub_directories tests/test_model.py::test_dump_should_raise_if_create_sub_directories_fails tests/test_model.py::test_dump_should_work_for_variant_streams tests/test_model.py::test_dump_should_work_for_variant_playlists_with_iframe_playlists tests/test_model.py::test_dump_should_work_for_iframe_playlists tests/test_model.py::test_dump_should_include_program_date_time tests/test_model.py::test_dump_should_not_ignore_zero_duration tests/test_model.py::test_dump_should_include_segment_level_program_date_time tests/test_model.py::test_dump_should_include_segment_level_program_date_time_without_discontinuity tests/test_model.py::test_dump_should_include_map_attributes tests/test_model.py::test_multiple_map_attributes tests/test_model.py::test_dump_should_include_multiple_map_attributes tests/test_model.py::test_dump_should_work_for_playlists_using_byteranges tests/test_model.py::test_should_dump_with_endlist_tag tests/test_model.py::test_should_dump_without_endlist_tag tests/test_model.py::test_should_dump_multiple_keys tests/test_model.py::test_should_dump_unencrypted_encrypted_keys_together tests/test_model.py::test_should_dump_complex_unencrypted_encrypted_keys tests/test_model.py::test_should_dump_complex_unencrypted_encrypted_keys_no_uri_attr tests/test_model.py::test_should_dump_session_data tests/test_model.py::test_should_dump_multiple_session_data tests/test_model.py::test_length_segments_by_key tests/test_model.py::test_list_segments_by_key tests/test_model.py::test_replace_segment_key tests/test_model.py::test_keyformat_and_keyformatversion tests/test_model.py::test_should_dump_program_datetime_and_discontinuity tests/test_model.py::test_should_normalize_segments_and_key_urls_if_base_path_passed_to_constructor tests/test_model.py::test_should_normalize_session_key_urls_if_base_path_passed_to_constructor tests/test_model.py::test_should_normalize_variant_streams_urls_if_base_path_passed_to_constructor tests/test_model.py::test_should_normalize_segments_and_key_urls_if_base_path_attribute_updated tests/test_model.py::test_playlist_type_dumped_to_appropriate_m3u8_field tests/test_model.py::test_empty_playlist_type_is_gracefully_ignored tests/test_model.py::test_none_playlist_type_is_gracefully_ignored tests/test_model.py::test_0_media_sequence_added_to_file tests/test_model.py::test_none_media_sequence_gracefully_ignored tests/test_model.py::test_0_discontinuity_sequence_added_to_file tests/test_model.py::test_none_discontinuity_sequence_gracefully_ignored tests/test_model.py::test_non_zero_discontinuity_sequence_added_to_file tests/test_model.py::test_should_correctly_update_base_path_if_its_blank tests/test_model.py::test_base_path_should_just_return_uri_if_absolute tests/test_model.py::test_m3u8_should_propagate_base_uri_to_segments tests/test_model.py::test_m3u8_should_propagate_base_uri_to_key tests/test_model.py::test_m3u8_should_propagate_base_uri_to_session_key tests/test_model.py::test_base_path_with_optional_uri_should_do_nothing tests/test_model.py::test_medialist_uri_method tests/test_model.py::test_segment_map_uri_attribute tests/test_model.py::test_segment_map_uri_attribute_with_byterange tests/test_model.py::test_start_with_negative_offset tests/test_model.py::test_start_with_precise tests/test_model.py::test_playlist_stream_info_contains_group_id_refs tests/test_model.py::test_should_dump_frame_rate tests/test_model.py::test_add_segment_to_playlist tests/test_model.py::test_segment_str_method tests/test_model.py::test_attribute_denormaliser tests/test_model.py::test_find_key_throws_when_no_match tests/test_model.py::test_ll_playlist tests/test_model.py::test_add_rendition_report_to_playlist tests/test_model.py::test_add_part_to_segment tests/test_model.py::test_partial_segment_gap_and_byterange tests/test_model.py::test_session_data_with_value tests/test_model.py::test_session_data_with_uri tests/test_model.py::test_session_data_cannot_be_created_with_value_and_uri_at_the_same_time tests/test_model.py::test_endswith_newline tests/test_model.py::test_init_section_base_path_update tests/test_model.py::test_iframe_playlists_base_path_update tests/test_model.py::test_partial_segment_base_path_update tests/test_model.py::test_add_preload_hint tests/test_model.py::test_add_daterange tests/test_model.py::test_daterange_simple tests/test_model.py::test_daterange_scte_out_and_in tests/test_model.py::test_daterange_enddate_sctecmd tests/test_model.py::test_daterange_in_parts tests/test_model.py::test_add_gap tests/test_model.py::test_gap tests/test_model.py::test_gap_in_parts