Integer.to_string_with_underscores?

Here’s a version that does it by calculating the offset and building up the string in a single pass.

  def annotate(str) do
    length = byte_size(str)
    offset = rem(length, 3)
    {acc, rest} = String.split_at(str, offset)
    do_annotate(acc, rest)
  end

  defp do_annotate(acc, ""), do: acc
  defp do_annotate("", <<next::binary-3, rest::binary>>), do: do_annotate(next, rest)

  defp do_annotate(acc, <<next::binary-3, rest::binary>>),
    do: do_annotate(<<acc::binary, ",", next::binary>>, rest)

In my benchmarks it’s about 3x faster than the formatter implementation for 7-digit strings (8x for 1kb strings but that’s probably not a realistic use-case). A fun little challenge but I know people aren’t a fan of the bitstring syntax :slight_smile:

Name                ips        average  deviation         median         99th %
adamu            5.46 M      183.02 ns  ±8688.03%         125 ns         250 ns
formatter        1.97 M      508.32 ns  ±3909.15%         334 ns         542 ns

Comparison:
adamu            5.46 M
formatter        1.97 M - 2.78x slower +325.30 ns

Memory usage statistics:

Name         Memory usage
adamu             0.40 KB
formatter         1.89 KB - 4.75x memory usage +1.49 KB

Operating System: macOS
CPU Information: Apple M1 Pro
Number of Available Cores: 10
Available memory: 16 GB
Elixir 1.15.4
Erlang 26.1

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 500 ms
reduction time: 0 ns
parallel: 1
inputs: none specified
Estimated total run time: 15 s