From bdeee2064b38847ed40e969a078fd2162f9e9329 Mon Sep 17 00:00:00 2001 From: Fabien Fleutot Date: Fri, 10 Feb 2017 11:21:47 +0100 Subject: [PATCH] add packing option to force float precision resolves #27. --- README.md | 16 +++++++++++++++- test_umsgpack.py | 14 ++++++++++++++ umsgpack.py | 30 +++++++++++++++++++++++++----- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f05149c..e12c86a 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,21 @@ b'\x80\x01\x02\x03' >>> ``` -### Compatibility Mode +### Float Precision + +The packing functions provide a `force_float_precision` option to force packing of floats into the specified precision: `"single"` for IEEE-754 single-precision floats, or `"double"` for IEEE-754 double-precision floats. + +``` python +>>> # Force float packing to single-precision floats +... umsgpack.packb(2.5, force_float_precision="single") +b'\xca@ \x00\x00' +>>> # Force float packing to double-precision floats +... umsgpack.packb(2.5, force_float_precision="double") +b'\xcb@\x04\x00\x00\x00\x00\x00\x00' +>>> +``` + +### Old Specification Compatibility Mode The compatibility mode supports the "raw" bytes MessagePack type from the [old specification](https://github.com/msgpack/msgpack/blob/master/spec-old.md). When the module-wide `compatibility` option is enabled, both unicode strings and bytes will be serialized into the "raw" MessagePack type, and the "raw" MessagePack type will be deserialized into bytes. diff --git a/test_umsgpack.py b/test_umsgpack.py index c16f3a1..a25b5b8 100644 --- a/test_umsgpack.py +++ b/test_umsgpack.py @@ -289,6 +289,11 @@ ["32-bit raw", b"b" * 65536, b"\xdb\x00\x01\x00\x00" + b"b" * 65536], ] +float_precision_test_vectors = [ + ["float precision single", 2.5, b"\xca\x40\x20\x00\x00"], + ["float precision double", 2.5, b"\xcb\x40\x04\x00\x00\x00\x00\x00\x00"], +] + CustomType = namedtuple('CustomType', ['x', 'y', 'z']) ext_handlers = { @@ -487,6 +492,15 @@ def test_unpack_ext_handler(self): unpacked = umsgpack.unpackb(data, ext_handlers=ext_handlers) self.assertEqual(unpacked, obj) + def test_pack_force_float_precision(self): + for ((name, obj, data), precision) in zip(float_precision_test_vectors, ["single", "double"]): + obj_repr = repr(obj) + print("\tTesting %s: object %s" % + (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + + packed = umsgpack.packb(obj, force_float_precision=precision) + self.assertEqual(packed, data) + def test_streaming_writer(self): # Try first composite test vector (_, obj, data) = composite_test_vectors[0] diff --git a/umsgpack.py b/umsgpack.py index 7169b6f..54da44c 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -271,10 +271,14 @@ def _pack_boolean(obj, fp, options): def _pack_float(obj, fp, options): - if _float_size == 64: + float_precision = options.get('force_float_precision', _float_precision) + + if float_precision == "double": fp.write(b"\xcb" + struct.pack(">d", obj)) - else: + elif float_precision == "single": fp.write(b"\xca" + struct.pack(">f", obj)) + else: + raise ValueError("invalid float precision") def _pack_string(obj, fp, options): @@ -381,6 +385,10 @@ def _pack2(obj, fp, **options): ext_handlers (dict): dictionary of Ext handlers, mapping a custom type to a callable that packs an instance of the type into an Ext object + force_float_precision (str): "single" to force packing floats as + IEEE-754 single-precision floats, + "double" to force packing floats as + IEEE-754 double-precision floats. Returns: None. @@ -447,6 +455,10 @@ def _pack3(obj, fp, **options): ext_handlers (dict): dictionary of Ext handlers, mapping a custom type to a callable that packs an instance of the type into an Ext object + force_float_precision (str): "single" to force packing floats as + IEEE-754 single-precision floats, + "double" to force packing floats as + IEEE-754 double-precision floats. Returns: None. @@ -512,6 +524,10 @@ def _packb2(obj, **options): ext_handlers (dict): dictionary of Ext handlers, mapping a custom type to a callable that packs an instance of the type into an Ext object + force_float_precision (str): "single" to force packing floats as + IEEE-754 single-precision floats, + "double" to force packing floats as + IEEE-754 double-precision floats. Returns: A 'str' containing serialized MessagePack bytes. @@ -541,6 +557,10 @@ def _packb3(obj, **options): ext_handlers (dict): dictionary of Ext handlers, mapping a custom type to a callable that packs an instance of the type into an Ext object + force_float_precision (str): "single" to force packing floats as + IEEE-754 single-precision floats, + "double" to force packing floats as + IEEE-754 double-precision floats. Returns: A 'bytes' containing serialized MessagePack bytes. @@ -946,7 +966,7 @@ def __init(): global load global loads global compatibility - global _float_size + global _float_precision global _unpack_dispatch_table global xrange @@ -955,9 +975,9 @@ def __init(): # Auto-detect system float precision if sys.float_info.mant_dig == 53: - _float_size = 64 + _float_precision = "double" else: - _float_size = 32 + _float_precision = "single" # Map packb and unpackb to the appropriate version if sys.version_info[0] == 3: