Вопрос
В Haskell библиотеки base и пакеты Hackage предоставляют несколько способов преобразования двоичных данных с плавающей запятой IEEE-754 в и с поднятых типов Float и Double. Однако точность, производительность и переносимость этих методов неясны.
Для библиотеки, ориентированной на GHC, предназначенной для (де) сериализации бинарного формата на разных платформах, наилучший подход для обработки данных с плавающей точкой IEEE-754?
Подходы
Это методы, с которыми я столкнулся в существующих библиотеках и онлайн-ресурсах.
FFI Marshaling
Это подход, используемый data-binary-ieee754. Поскольку Float, Double, Word32 и Word64 - это каждый экземпляр Storable, можно указать poke значение типа источника во внешний буфер, а затем peek значение целевой Тип:
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
На моей машине это работает, но я сжимаю, чтобы видеть, что распределение выполняется только для того, чтобы добиться принуждения. Кроме того, хотя это и не является уникальным для этого решения, здесь подразумевается предположение о том, что IEEE-754 фактически является представлением в памяти. Тестирование, сопровождающее упаковку, дает одобрение печати "работает на моей машине", но это не идеально.
unsafeCoerce
При таком же неявном допущении в представлении IEEE-754 в памяти следующий код также получает печать "работ на моей машине":
toFloat :: Word32 -> Float
toFloat = unsafeCoerce
Это позволяет не выполнять явное распределение, как описанный выше подход, но документация говорит: "Вы несете ответственность за то, чтобы старые и новые типы имеют идентичные внутренние представления". Это подразумеваемое предположение все еще выполняет всю работу и еще более напряжено при работе с отмененными типами.
unsafeCoerce#
Растяжение пределов того, что можно считать "переносимым":
toFloat :: Word -> Float
toFloat (W# w) = F# (unsafeCoerce# w)
Это, похоже, работает, но не кажется практичным вообще, поскольку оно ограничено типами GHC.Exts. Приятно обходить поднятые типы, но это обо всем, что можно сказать.
encodeFloat и decodeFloat
Этот подход имеет приятное свойство обойти что-либо с unsafe в названии, но, похоже, не совсем корректен для IEEE-754. A предыдущий SO ответ по аналогичному вопросу предлагает краткий подход, а ieee754-parser используется более общий подход, прежде чем устаревать в пользу data-binary-ieee754.
Здесь довольно немного привлекает код, который не нуждается в неявных предположениях о базовом представлении, но эти решения полагаются на encodeFloat и decodeFloat, которые, по-видимому, чревато несогласованностью. Я еще не нашел пути решения этих проблем.