Source code for hbutils.binary.float

"""
Binary floating-point type utilities for structured I/O.

This module provides floating-point type definitions for binary input/output
operations based on the :mod:`struct` module. It defines convenience classes and
instances for standard IEEE-754 floating-point sizes (16-bit, 32-bit, 64-bit),
along with aliases matching the conventional C type names.

The module exposes the following public components:

* :class:`CFloatType` - Float type wrapper with binary read/write helpers
* :data:`c_float16` - 16-bit (half precision) float type instance
* :data:`c_float32` - 32-bit (single precision) float type instance
* :data:`c_float64` - 64-bit (double precision) float type instance
* :data:`c_float` - Alias for :data:`c_float32`
* :data:`c_double` - Alias for :data:`c_float64`

Example::

    >>> import io
    >>> from hbutils.binary.float import c_float32
    >>>
    >>> buffer = io.BytesIO()
    >>> c_float32.write(buffer, 3.5)
    >>> buffer.seek(0)
    0
    >>> c_float32.read(buffer)
    3.5

.. note::
   These types are thin wrappers around :class:`hbutils.binary.base.CMarkedType`,
   which uses the :mod:`struct` module for binary packing and unpacking.

"""

import ctypes
from typing import Any, BinaryIO, Dict, List, Type, Union

from .base import CMarkedType

__all__ = [
    'CFloatType',
    'c_float16', 'c_float32', 'c_float64',
    'c_float', 'c_double',
]


[docs] class CFloatType(CMarkedType): """ Float type class for binary I/O operations, based on the ``struct`` module. This class extends :class:`hbutils.binary.base.CMarkedType` to provide specialized handling for floating-point numbers in binary format. It enforces conversion to ``float`` when writing values. Example:: >>> import io >>> from hbutils.binary.float import CFloatType >>> >>> float_type = CFloatType('f', 4) >>> buffer = io.BytesIO() >>> float_type.write(buffer, 1.25) >>> buffer.seek(0) 0 >>> float_type.read(buffer) 1.25 """
[docs] def read(self, file: BinaryIO) -> float: """ Read a floating-point value from a binary file. :param file: The binary file to read from. :type file: BinaryIO :return: The floating-point value read from the file. :rtype: float """ return super().read(file)
[docs] def write(self, file: BinaryIO, val: Union[int, float]) -> None: """ Write a floating-point value to a binary file. :param file: The binary file to write to. :type file: BinaryIO :param val: The numeric value to write (will be converted to float). :type val: Union[int, float] :return: ``None``. :rtype: None """ super().write(file, float(val))
c_float16 = CFloatType('e', 2) """ Reading and writing half-precision (16-bit) floating-point numbers. This type uses 2 bytes to represent floating-point values with reduced precision. Example:: >>> import io >>> from hbutils.binary.float import c_float16 >>> >>> buffer = io.BytesIO() >>> c_float16.write(buffer, 1.5) >>> buffer.seek(0) 0 >>> c_float16.read(buffer) 1.5 """ c_float32 = CFloatType('f', 4) """ Reading and writing single-precision (32-bit) floating-point numbers. This type uses 4 bytes to represent floating-point values. Examples:: >>> import io >>> import math >>> from hbutils.binary import c_float32 >>> >>> with io.BytesIO(b'\\x00\\x00\\x90\\x7f' ... b'\\x00\\x00\\x80\\x7f' ... b'\\x00\\xa0\\x3e\\xc1' ... b'\\x00\\x00\\x70\\x00') as file: ... print(c_float32.read(file)) ... print(c_float32.read(file)) ... print(c_float32.read(file)) ... print(c_float32.read(file)) nan inf -11.9140625 1.0285575569695016e-38 >>> with io.BytesIO() as file: ... c_float32.write(file, math.nan) ... c_float32.write(file, +math.inf) ... c_float32.write(file, -11.9140625) ... c_float32.write(file, 1.0285575569695016e-38) ... print(file.getvalue()) b'\\x00\\x00\\xc0\\x7f\\x00\\x00\\x80\\x7f\\x00\\xa0>\\xc1\\x00\\x00p\\x00' """ c_float64 = CFloatType('d', 8) """ Reading and writing double-precision (64-bit) floating-point numbers. This type uses 8 bytes to represent floating-point values with higher precision. Examples:: >>> import io >>> import math >>> from hbutils.binary import c_float64 >>> >>> with io.BytesIO(b'\\x00\\x00\\x00\\x00\\x00\\x00\\xf8\\x7f' ... b'\\x00\\x00\\x00\\x00\\x00\\x00\\xf0\\x7f' ... b'\\x00\\x00\\x00\\x00\\x00\\xd4\\x27\\xc0' ... b'\\x00\\x00\\x00\\x00\\x00\\x00\\x0c8') as file: ... print(c_float64.read(file)) ... print(c_float64.read(file)) ... print(c_float64.read(file)) ... print(c_float64.read(file)) nan inf -11.9140625 1.0285575569695016e-38 >>> with io.BytesIO() as file: ... c_float64.write(file, math.nan) ... c_float64.write(file, +math.inf) ... c_float64.write(file, -11.9140625) ... c_float64.write(file, 1.0285575569695016e-38) ... print(file.getvalue()) b"\\x00\\x00\\x00\\x00\\x00\\x00\\xf8\\x7f\\x00\\x00\\x00\\x00\\x00\\x00\\xf0\\x7f\\x00\\x00\\x00\\x00\\x00\\xd4'\\xc0\\x00\\x00\\x00\\x00\\x00\\x00\\x0c8" """ _EXIST_TYPES: List[CFloatType] = [ c_float16, c_float32, c_float64, ] """List of all available floating-point types.""" _SIZE_TO_FLOAT_TYPE: Dict[int, CFloatType] = { item.size: item for item in _EXIST_TYPES } """Mapping from byte size to corresponding CFloatType instance.""" def _get_from_raw(tp: Type[ctypes._SimpleCData]) -> CFloatType: """ Get the corresponding :class:`CFloatType` from a ``ctypes`` float type. :param tp: A ctypes float type (e.g., :class:`ctypes.c_float`, :class:`ctypes.c_double`). :type tp: Type[ctypes._SimpleCData] :return: The corresponding :class:`CFloatType` instance. :rtype: CFloatType """ return _SIZE_TO_FLOAT_TYPE[ctypes.sizeof(tp)] c_float = _get_from_raw(ctypes.c_float) """ Alias for :data:`c_float32`. This provides a convenient name matching the C language's ``float`` type. """ c_double = _get_from_raw(ctypes.c_double) """ Alias for :data:`c_float64`. This provides a convenient name matching the C language's ``double`` type. """