Skip to content

Instantly share code, notes, and snippets.

@mneumann
Last active September 9, 2022 09:36
Show Gist options
  • Save mneumann/a34c1823f89cfd18cd49eeae89d46ccf to your computer and use it in GitHub Desktop.
Save mneumann/a34c1823f89cfd18cd49eeae89d46ccf to your computer and use it in GitHub Desktop.
Convert number into decimal representation via packed BCD
# Convert number into decimal representation intermediate packed BCD.
# Uses two 64-bit integers
def assert_eq(a, b)
raise "#{a} != #{b}" unless a == b
end
def number_to_packed_bcd_int(n)
raise if n > (10**32) - 1
# Use two u64
bcd_high, bcd_low = 0, 0
ndigits = 0
loop do
ndigits += 1
digit = n % 10
n /= 10
bcd_high = (bcd_high << 4) & 0xFFFFFFFF_FFFFFFFF
bcd_high = bcd_high | (bcd_low >> 60) & 0xF
bcd_low = (bcd_low << 4) & 0xFFFFFFFF_FFFFFFFF
bcd_low = bcd_low | digit
break if n == 0
end
[ndigits, bcd_high, bcd_low]
end
def packed_bcd_int_to_string(ndigits, bcd_high, bcd_low)
raise if ndigits == 0
s = String.new("", capacity: ndigits)
ndigits.times do
digit = bcd_low & 0xF
s << ('0'.ord + digit).chr
bcd_low = (bcd_low >> 4) | (bcd_high & 0xF) << 60
bcd_high = bcd_high >> 4
end
s
end
def to_decimal_s(n)
packed_bcd_int_to_string(*number_to_packed_bcd_int(n))
end
assert_eq(number_to_packed_bcd_int(0), [1, 0, 0])
assert_eq(number_to_packed_bcd_int(9), [1, 0, 9])
assert_eq(number_to_packed_bcd_int(10), [2, 0, 1])
assert_eq(number_to_packed_bcd_int(56), [2, 0, 5 | (6 << 4)])
assert_eq(number_to_packed_bcd_int(100), [3, 0, 1])
assert_eq(number_to_packed_bcd_int(12345), [5, 0, 1 | (2 << 4) | (3 << 8) | (4 << 12) | (5 << 16)])
assert_eq(packed_bcd_int_to_string(2, 0, 5 | (6 << 4)), "56")
assert_eq(to_decimal_s(12345), "12345")
1000.times do
n = rand(1_000_000_000_000)
assert_eq(to_decimal_s(n), n.to_s(10))
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment