Формула для преобразования байтового массива, представляющего целое число со знаком, в целое число - PullRequest
1 голос
/ 19 марта 2019

Этот вопрос более общий без определенного языка.Меня больше интересует решение этой проблемы в целом на разных языках.Каждый найденный ответ ссылается на встроенный метод типа getInt32 для извлечения целого числа из байтового массива.

У меня есть байтовый массив, который содержит представление с прямым порядком байтов целого числа со знаком.

1 -> [0, 0, 0, 1]
-1 -> [255, 255, 255, 255]
-65535 -> [255, 255, 0, 1]

Получить значения для положительных случаев очень просто:

arr[3] | arr[2] << 8 | arr[1] << 16 | arr[0] << 24

То, что я хотел бы выяснить, это более общий случай.Я читал о дополнении 2s, которое привело меня к функции python из Википедии:

def twos_complement(input_value, num_bits):
    '''Calculates a two's complement integer from the given input value's bits'''
    mask = 2**(num_bits - 1) - 1
    return -(input_value & mask) + (input_value & ~mask)

, что в свою очередь привело меня к созданию этой функции:

# Note that the mask from the wiki function has an additional - 1
mask = 2**(32 - 1)
def arr_to_int(arr):
    uint_val = arr[3] | arr[2] << 8 | arr[1] << 16 | arr[0] << 24
    if (determine_if_negative(uint_val)):
        return -(uint_val & mask) + (uint_val & ~mask)
    else:
        return uint_val

Для того, чтобы мойДля работы функции мне нужно заполнить determine_if_negative (я должен замаскировать бит со знаком и проверить, равен ли он 1).Но есть ли стандартная формула для этого?Одна вещь, которую я обнаружил, заключается в том, что в некоторых языках, таких как Go, битовое смещение может переполнять значение int.

Это довольно сложно найти, потому что я получаю тысячу результатов, объясняющих разницу между старшим и младшим порядком байтов, или результаты, объясняющие дополнение к двум, и еще много примеров использования стандартной библиотеки, но я невидел полную формулу для побитовых функций.

Есть ли канонический пример в C или аналогичном языке преобразования массива char с использованием только доступа к массиву и побитовых функций (т. е. без memcpy или приведения указателей или хитрых вещей)

Ответы [ 2 ]

0 голосов
/ 19 марта 2019

Даже с может быть слишком высоким уровнем для этого.В конце концов, точное представление int зависит от машины.Кроме того, не все целочисленные типы во всех системах являются дополнением к 2s.

Когда вы упоминаете байтовый массив и конвертируете его в целое число, вы должны указать, какой формат подразумевает этот байтовый массив.

Если вы предполагаете, что 2s дополняют и имеют младший порядковый номер (например, intel / amd).Тогда последний байт содержит знак.

Для простоты давайте начнем с 4-значного целого числа, дополненного 2-мя байтами, затем байтового байта, затем 2-байтовых целых чисел и затем 4.

BIN SIGNED_DEC   UNSIGNED_DEC
000   0             0            
001   1             1
010   2             2
100   -4(oops)      4
101   -3            5
110   -1            6
111   -1            7
---
123

бит будет b3, b2, b1, где b1 - старший значащий бит (и знак), тогда формула будет иметь вид:

b3*2^2+b2*2^1-b1*4

для байта у нас есть 4 бита, и формула будет выглядеть так:

b4*2^3 + b3*2^2+b2*2^1-b1*2^3

для 2 байтов это то же самое, но мы должны умножить старший значащий байт на 256, и отрицательное значение будет 256 ^ 2 или 2 ^ 16.

  /**
   * returns calculated value of 2s complement bit string.
   * expects string of bits 0or1. if a chanracter is not 1 it is considered 0.
   * 
   */
  public static long twosComplementFromBitArray(String input) {
    if(input.length()<2) throw new RuntimeException("intput too short ");
    int sign=input.charAt(0)=='1'?1:0;
    long unsignedComplementSum=1;
    long unsignedSum=0;

    for(int i=1;i<input.length();++i) {
      char c=input.charAt(i);
      int val=(c=='1')?1:0;
      unsignedSum=unsignedSum*2+val;
      unsignedComplementSum*=2;
    }
    return unsignedSum-sign*unsignedComplementSum;
  }
  public static void main(String[] args) {
    System.out.println(twosComplementFromBitArray("000"));
    System.out.println(twosComplementFromBitArray("001"));
    System.out.println(twosComplementFromBitArray("010"));
    System.out.println(twosComplementFromBitArray("011"));
    System.out.println(twosComplementFromBitArray("100"));
    System.out.println(twosComplementFromBitArray("101"));
    System.out.println(twosComplementFromBitArray("110"));
    System.out.println(twosComplementFromBitArray("111"));
  }

выходных данных:

 0
 1
 2
 3
-4
-3
-2
-1
0 голосов
/ 19 марта 2019

Битовый метод работает правильно только для беззнаковых значений, поэтому вам нужно будет построить целое число без знака, а затем преобразовать в знаковое.Код может быть:

int32_t val( uint8_t *s )
{
    uint32_t x = ((uint32_t)s[0] << 24) + ((uint32_t)s[1] << 16) + ((uint32_t)s[2] << 8) + s[3];
    return x;
}

Обратите внимание, это предполагает, что вы находитесь в системе дополнения 2, которая также определяет unsigned-> подписанное преобразование как отсутствие изменений в представлении.Если вы хотите поддерживать и другие системы, это будет сложнее.

Приведения необходимы для того, чтобы сдвиг выполнялся по правильной ширине.

...