Переполнения стека являются ошибкой в логике.
countOnes !a !b | a == b = countOnes' a
countOnes' :: Int64 -> Integer
countOnes' !0 = 0
countOnes' !a = (fromIntegral (a .&. 1)) + (countOnes' (a `shiftR` 1))
Всякий раз, когда вы вызываете countOnes'
с отрицательным аргументом, вы получаете не окончательное вычисление, поскольку shiftR
- это арифметический сдвиг, а нелогический, так что вы всегда сдвигаете в 1 бит и никогда не достигаете 0. Но даже при логическом сдвиге для отрицательных аргументов вы получите результат 32 слишком большой, поскольку все старшие 32 бита равны 1.
Решение: замаскируйте неинтересные биты перед вызовом countOnes'
,
countOnes !a !b | a == b = countOnes' (a .&. 0xFFFFFFFF)
В countOnes
,
countOnes :: Int64 -> Int64 -> Integer
countOnes !a !b | a > b = 0
-- From here on we know a <= b
countOnes !a !b | a == b = countOnes' (a .&. 0xFFFFFFFF)
-- From here on, we know a < b
countOnes !0 !n = range + leading + (countOnes 0 (n - (1 `shiftL` m)))
where
range = fromIntegral $ m * (1 `shiftL` (m - 1))
leading = fromIntegral $ (n - (1 `shiftL` m) + 1)
m = (getLog n) - 1
-- From here on, we know a /= 0
countOnes !a !b | a > 0 = (countOnes 0 b) - (countOnes 0 (a - 1))
-- From here on, we know a < 0,
-- the guard in the next and the last equation are superfluous
countOnes !a !0 | a < 0 = countOnes (maxInt + a + 1) maxInt
countOnes !a !b | b < 0 = (countOnes a 0) - (countOnes (b + 1) 0)
countOnes !a !b | a < 0 = (countOnes a 0) + (countOnes 0 b)
есть некоторые лишние элементы защитыСервер вызван
getLog :: Int64 -> Int
--
countOnes !0 !n = range + leading + (countOnes 0 (n - (1 `shiftL` m)))
where
range = fromIntegral $ m * (1 `shiftL` (m - 1))
leading = fromIntegral $ (n - (1 `shiftL` m) + 1)
m = (getLog n) - 1
, потому что сервер имеет 32-разрядный GHC, в то время как у вас есть 64-разрядный.Расстояние сдвига / ширина в битах m
- это Int
(и поскольку оно используется в качестве расстояния сдвига, оно должно быть).
Следовательно,
m * (1 `shiftL` (m-1))
- это Int
тоже.Для m >= 28
, который переполняет 32-битный Int
.
Решение: удалить $
range = fromIntegral m * (1 `shiftL` (m - 1))
Тогда смещенное 1
будет Integer
следовательно, переполнения нет.