Skip to content

PROV-O (RDF) round-trip loses xsd:float precision (canonicalised to short decimal) #225

Description

@trungdong

Found by the Task 13 Hypothesis property-based round-trip tests (src/prov/tests/test_property_roundtrip.py).

A Python float attribute value is typed by prov as xsd:float (single precision). The RDF serializer canonicalises xsd:float to a short decimal literal, so a value whose exact decimal expansion is longer than that short form comes back changed after an RDF round trip. PROV-JSON and PROV-XML preserve the full repr and are unaffected.

Reproduction:

import io, struct
from prov.model import ProvDocument

def rt(d, fmt):
    s = io.BytesIO(); d.serialize(destination=s, format=fmt, indent=4); s.seek(0)
    return ProvDocument.deserialize(source=s, format=fmt)

def f32(x):  # nearest float32, as a Python float
    return struct.unpack("f", struct.pack("f", x))[0]

for v in [f32(0.1), f32(1.7), 0.125, -12.5]:
    d = ProvDocument(); d.add_namespace("ex", "http://example.org/")
    d.entity("ex:e0", {"ex:k": v})
    print(f"{v!r:24} rdf:{rt(d,rdf)==d}")
# 0.10000000149011612   rdf:False   (0.1 -> "1e-01" -> 0.1 as float64)
# 1.7000000476837158    rdf:False
# 0.125                 rdf:True    (dyadic, exact short decimal)
# -12.5                 rdf:True

Values that are exactly float32-representable and have a short exact decimal (dyadic rationals) survive; others do not. Related to #77 (xsd:decimal fidelity loss through RDF), but this is the xsd:float path.

Status: deferred to 3.0.0 under the 2.x output freeze. Excluded from Hypothesis generation by restricting float values to eighth-step dyadic rationals of modest magnitude (src/prov/tests/strategies.py), commented pointing here. No behaviour change made in 2.x.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions