Splitting big.Int by digit

Issue

I’m trying to split a big.Int into a number of int64s such that each is a portion of the larger number, with a standard offset of 18 digits. For example, given the following input value of 1234512351234088800000999, I would expect the following output: [351234088800000999, 1234512]. For negative numbers, I would expect all of the parts to be negative (i.e. -1234512351234088800000999 produces [-351234088800000999, -1234512]).

I already know I can do this to get the result I want:

func Split(input *big.Int) []int64 {
    const width = 18

    asStr := in.Coefficient().Text(10)
    strLen := len(asStr)
    offset := 0
    if in.IsNegative() {
        offset = 1
    }

    length := int(math.Ceil(float64(strLen-offset) / width))
    ints := make([]int64, length)
    for i := 1; i <= length; i++ {
        start := strLen - (i * width)
        end := start + width
        if start < 0 || (start == 1 && asStr[0] == '-') {
            start = 0
        }

        ints[i-1], _ = strconv.ParseInt(asStr[start:end], 10, 64)
        if offset == 1 && ints[i-1] > 0 {
            ints[i-1] = 0 - ints[i-1]
        }
    }

    return ints
}

However, I don’t like the idea of using string-parsing nor do I like the use of strconv. Is there a way I can do this utilizing the big.Int directly?

Solution

You can use the DivMod function to do what you need here, with some special care to handle negative numbers:

var offset = big.NewInt(1e18)

func Split(input *big.Int) []int64 {
    rest := new(big.Int)
    rest.Abs(input)

    var ints []int64
    r := new(big.Int)
    for {
        rest.DivMod(rest, offset, r)
        ints = append(ints, r.Int64() * int64(input.Sign()))
        if rest.BitLen() == 0 {
            break
        }
    }
    return ints
}

Note that it’s not clear exactly what should happen for negative numbers. Depending on what you want, you might want to just flip the sign on the first output, or the last, or something else. In the code here, I make all the output values negative if the input was negative so that the sum of the output values multiplied by 1e18 times their position in the output equals the input.

Answered By – Austin

Answer Checked By – Terry (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.