Преобразовать отрицательную цифру в байтовом срезе в int - PullRequest
0 голосов
/ 18 декабря 2018

Мне нужна помощь

Дебезиум (https://debezium.io/) преобразование десятичных цифр в целое число в строке base64 с точностью в качестве параметра в json. В Java это декодируется, как в этом примере

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Base64;

public class HelloWorld{

     public static void main(String []args){
        String encoded = "lTBA";
        int scale = 4;
        final BigDecimal decoded = new BigDecimal(new BigInteger(Base64.getDecoder().decode(encoded)), scale);
        System.out.println(decoded);        
     }
}

"lTBA" = -700

но в Голанге у меня есть некоторые проблемы в аналогичном примере

основной пакет

import (
    "encoding/base64"
    "fmt"
    "math/big"
)

func main() {
    data, _ := base64.StdEncoding.DecodeString("lTBA")

    bigInt := new(big.Int).SetBytes(data).String()
    fmt.Println(bigInt)
}

https://play.golang.org/p/3Xdq9x6B9V- Возвращает 9777216

Потому что, когда я использую SetBytes в «math / big / Int», он всегда задает положительную цифру

// SetBytes interprets buf as the bytes of a big-endian unsigned
// integer, sets z to that value, and returns z.
func (z *Int) SetBytes(buf []byte) *Int {
    z.abs = z.abs.setBytes(buf)
    z.neg = false
    return z
}

1 Ответ

0 голосов
/ 18 декабря 2018

Конструктор Java BigInteger ожидает число в двоичном представлении дополнения 2, в то время как Go * big.Int.SetBytes() ожидает целое значение без знака (в большомпорядковый номер байта):

SetBytes интерпретирует buf как байты с прямым порядком байтов целое число без знака , устанавливает z в это значение и возвращает z.

То, что мы можем сделать, - это использовать Int.SetBytes(), но если число отрицательное, мы должны преобразовать его в число, двоичные данные, представленные в дополнении к 2.Мы можем сказать, является ли он отрицательным, если его первый бит равен 1 (что является старшим битом первого байта).

Это преобразование простое: если входные данные представлены с использованием n байтов, сконструируйте число, используяn+1 байт, где первый 1, остальные 0 (максимальное представимое число, использующее n байт плюс 1).Вычитая (Int.Sub()) из этого числа, вы получите абсолютное значение числа в дополнении к 2, поэтому нам просто нужно применить отрицательный знак к этому числу: Int.Neg().

Шкалу можно применить, разделив (Int.Div()) результат на число, равное 10scale.Мы можем построить такое число, добавив scale нулей к 1.

Вот функция decode(), которая делает все это.Он не оптимизирован для производительности, но выполняет свою работу:

func decode(in string, scale int) (out *big.Int, err error) {
    data, err := base64.StdEncoding.DecodeString(in)
    if err != nil {
        return
    }

    out = new(big.Int).SetBytes(data)

    // Check if negative:
    if len(data) > 0 && data[0]&0x80 != 0 {
        // It's negative.
        // Convert 2's complement negative to abs big-endian:
        data2 := make([]byte, len(data)+1)
        data2[0] = 1
        temp := new(big.Int).SetBytes(data2)
        out.Sub(temp, out)

        // Apply negative sign:
        out.Neg(out)
    }

    // Apply scale:
    if scale > 0 {
        temp, _ := new(big.Int).SetString("1"+strings.Repeat("0", scale), 10)
        out.Div(out, temp)
    }

    return
}

Пример тестирования:

n, err := decode("lTBA", 4)
fmt.Println(n, err)

Вывод (попробуйте на Go Playground ):

-700 <nil>
...