Question
En Haskell, le base
et les paquets Hackage fournissent plusieurs moyens de convertir des données binaires en virgule flottante IEEE-754 vers et à partir du format lifté. Float
et Double
types. Cependant, la précision, la performance et la portabilité de ces méthodes ne sont pas claires.
Pour une bibliothèque ciblant GHC et destinée à (dé)sérialiser un format binaire sur plusieurs plateformes, quelle est la meilleure approche pour gérer les données en virgule flottante IEEE-754 ?
Approches
Ce sont les méthodes que j'ai rencontrées dans les bibliothèques existantes et les ressources en ligne.
FFI Marshaling
C'est l'approche utilisée par le data-binary-ieee754
paquet. Depuis Float
, Double
, Word32
et Word64
sont chacune des instances de Storable
on peut poke
une valeur du type source dans un tampon externe, puis peek
une valeur du type cible :
toFloat :: (F.Storable word, F.Storable float) => word -> float
toFloat word = F.unsafePerformIO $ F.alloca $ \buf -> do
F.poke (F.castPtr buf) word
F.peek buf
Sur ma machine, cela fonctionne, mais je crains de voir l'allocation effectuée juste pour accomplir la coercition. De plus, bien que cette solution ne soit pas unique, il y a une hypothèse implicite selon laquelle IEEE-754 est en fait la représentation en mémoire. Les tests accompagnant le paquet lui donnent le sceau d'approbation "fonctionne sur ma machine", mais ce n'est pas idéal.
unsafeCoerce
Avec la même hypothèse implicite de représentation IEEE-754 en mémoire, le code suivant obtient également le label "fonctionne sur ma machine" :
toFloat :: Word32 -> Float
toFloat = unsafeCoerce
Cette méthode présente l'avantage de ne pas effectuer d'allocation explicite comme l'approche ci-dessus, mais la documentation dit "il est de votre responsabilité de vous assurer que les anciens et les nouveaux types ont des représentations internes identiques". Cette hypothèse implicite fait toujours tout le travail, et elle est encore plus contraignante lorsqu'il s'agit de types levés.
unsafeCoerce#
Repousser les limites de ce qui peut être considéré comme "portable" :
toFloat :: Word -> Float
toFloat (W# w) = F# (unsafeCoerce# w)
Cela semble fonctionner, mais n'est pas du tout pratique puisque cela se limite à l'écran de l'ordinateur. GHC.Exts
types. Il est agréable de contourner les types levés, mais c'est à peu près tout ce qu'on peut dire.
encodeFloat
et decodeFloat
Cette approche a l'agréable propriété de contourner tout ce qui a trait à l'utilisation de la fonction unsafe
dans le nom, mais ne semble pas obtenir IEEE-754 tout à fait juste. A réponse précédente du SO à une question similaire offre une approche concise, et la ieee754-parser
utilisait une approche plus générale avant d'être déprécié en faveur du paquet data-binary-ieee754
.
L'intérêt de disposer d'un code qui ne nécessite pas d'hypothèses implicites sur la représentation sous-jacente n'est pas négligeable, mais ces solutions s'appuient sur encodeFloat
et decodeFloat
qui sont apparemment plein d'incohérences . Je n'ai pas encore trouvé le moyen de contourner ces problèmes.