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