Source code for pystructs.fields.floats

"""Floating-point field types."""

from __future__ import annotations

import struct
from typing import TYPE_CHECKING, BinaryIO, Callable, List

from pystructs.base import FixedField
from pystructs.config import Endian
from pystructs.exceptions import UnexpectedEOF

if TYPE_CHECKING:
    from pystructs.struct import Struct

__all__ = (
    "Float32",
    "Float64",
)


[docs] class Float32(FixedField): """32-bit floating point number (IEEE 754 single precision). Examples: >>> class Point(Struct): ... x = Float32() ... y = Float32() """ size = 4 def __init__( self, endian: str | None = None, default: float | None = None, required: bool = True, validators: List[Callable] | None = None, ): """Initialize a Float32 field. Args: endian: Byte order ('little' or 'big'), or None to use struct default default: Default value required: If True, field must have a value for serialization validators: List of validator functions """ super().__init__(default=default, required=required, validators=validators) self.endian = endian def _get_endian(self, instance: Struct) -> str: """Get the effective endianness for this field. Args: instance: The struct instance Returns: The endianness to use ('little' or 'big') """ if self.endian is not None: return self.endian return instance._meta.endian def parse(self, buffer: BinaryIO, instance: Struct) -> float: """Parse a 32-bit float from buffer. Args: buffer: Binary stream to read from instance: The struct instance being parsed Returns: Parsed float value Raises: UnexpectedEOF: If not enough bytes available """ data = buffer.read(4) if len(data) < 4: raise UnexpectedEOF(expected=4, actual=len(data)) endian = self._get_endian(instance) fmt = "<f" if endian == Endian.LITTLE else ">f" return struct.unpack(fmt, data)[0] def serialize(self, value: float, instance: Struct) -> bytes: """Serialize a 32-bit float to bytes. Args: value: The float value to serialize instance: The struct instance being serialized Returns: Serialized bytes """ if value is None: value = 0.0 endian = self._get_endian(instance) fmt = "<f" if endian == Endian.LITTLE else ">f" return struct.pack(fmt, value)
[docs] class Float64(FixedField): """64-bit floating point number (IEEE 754 double precision). Examples: >>> class Point3D(Struct): ... x = Float64() ... y = Float64() ... z = Float64() """ size = 8 def __init__( self, endian: str | None = None, default: float | None = None, required: bool = True, validators: List[Callable] | None = None, ): """Initialize a Float64 field. Args: endian: Byte order ('little' or 'big'), or None to use struct default default: Default value required: If True, field must have a value for serialization validators: List of validator functions """ super().__init__(default=default, required=required, validators=validators) self.endian = endian def _get_endian(self, instance: Struct) -> str: """Get the effective endianness for this field. Args: instance: The struct instance Returns: The endianness to use ('little' or 'big') """ if self.endian is not None: return self.endian return instance._meta.endian def parse(self, buffer: BinaryIO, instance: Struct) -> float: """Parse a 64-bit float from buffer. Args: buffer: Binary stream to read from instance: The struct instance being parsed Returns: Parsed float value Raises: UnexpectedEOF: If not enough bytes available """ data = buffer.read(8) if len(data) < 8: raise UnexpectedEOF(expected=8, actual=len(data)) endian = self._get_endian(instance) fmt = "<d" if endian == Endian.LITTLE else ">d" return struct.unpack(fmt, data)[0] def serialize(self, value: float, instance: Struct) -> bytes: """Serialize a 64-bit float to bytes. Args: value: The float value to serialize instance: The struct instance being serialized Returns: Serialized bytes """ if value is None: value = 0.0 endian = self._get_endian(instance) fmt = "<d" if endian == Endian.LITTLE else ">d" return struct.pack(fmt, value)