Skip to content

Commit

Permalink
add packing option to force float precision
Browse files Browse the repository at this point in the history
resolves #27.
  • Loading branch information
Fabien Fleutot authored and vsergeev committed Apr 19, 2017
1 parent 5f53bcf commit bdeee20
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 6 deletions.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
14 changes: 14 additions & 0 deletions test_umsgpack.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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]
Expand Down
30 changes: 25 additions & 5 deletions umsgpack.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -946,7 +966,7 @@ def __init():
global load
global loads
global compatibility
global _float_size
global _float_precision
global _unpack_dispatch_table
global xrange

Expand All @@ -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:
Expand Down

0 comments on commit bdeee20

Please sign in to comment.