Вопрос
В 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
, которые, по-видимому, чревато несогласованностью. Я еще не нашел пути решения этих проблем.