Целочисленная факторизация для этого далеко не обязательна.
Показатель степени в основном будет полем логарифма с базой 2, который не так сложно вычислить.
Следующий код проходит тесты QuickCheck, а также тесты на бесконечность и отрицательную бесконечность:
minNormalizedDouble :: Double
minNormalizedDouble = 2 ^^ (-1022)
powers :: [(Int, Double)]
powers = [(b, 2.0 ^^ fromIntegral b) | i <- [9, 8..0], let b = bit i]
exponentOf :: Double -> Int
exponentOf d
| d < 0 = exponentOf (-d)
| d < minNormalizedDouble = -1024
| d < 1 =
let go (dd, accum) (p, twoP)
| dd * twoP < 1 = (dd * twoP, accum - p)
| otherwise = (dd, accum)
in snd $ foldl' go (d, 0) powers
| otherwise =
let go (x, accum) (p, twoP)
| x * twoP <= d = (x * twoP, accum + p)
| otherwise = (x, accum)
in 1 + (snd $ foldl' go (1.0, 0) powers)
decode :: Double -> (Integer, Int)
decode 0.0 = (0, 0)
decode d
| isInfinite d, d > 0 = (4503599627370496, 972)
| isInfinite d, d < 0 = (-4503599627370496, 972)
| isNaN d = (-6755399441055744, 972)
| otherwise =
let
e = exponentOf d - 53
twoE = 2.0 ^^ e
in (round (d / twoE), e)
Я протестировал его, используя quickCheck (\ d -> decodeFloat d == decode d)
, и явно протестировал его отдельно на положительных и отрицательных бесконечностях.
Единственные примитивные операции, используемые здесь, это сдвиг влево, двойное умножение, двойное деление, тестирование на бесконечность и NaN, которые Javascript поддерживает, насколько мне известно.