Передайте два целых числа как одно целое - PullRequest
11 голосов
/ 19 января 2011

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

Я думаю об использовании логических операторов (И, ИЛИ, XOR и т. Д.).

Ответы [ 5 ]

12 голосов
/ 19 января 2011

Используя язык программирования C, это можно сделать следующим образом, предполагая, что два целых числа меньше 65535.

void take2IntegersAsOne(int x)
{
   // int1 is stored in the bottom half of x, so take just that part.
   int int1 = x & 0xFFFF;  

   // int2 is stored in the top half of x, so slide that part of the number
   // into the bottom half, and take just that part.
   int int2 = (x >> 16) & 0xFFFF

   // use int1 and int2 here. They must both be less than 0xFFFF or 65535 in decimal

}


void pass2()
{
  int int1 = 345;
  int int2 = 2342;
  take2Integers( int1 | (int2 << 16) );
}

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

Операторы сдвига << и >> используются для перемещения битов целого числа вверх и вниз. Сдвиг на 16, перемещение битов на два байта (так как на каждый байт приходится 8 бит).

Использование 0xFFFF представляет битовую комбинацию, в которой все биты в двух младших байтах числа равны 1 с. Таким образом, ANDing (с оператором &) отключает все биты, которые не находятся в этих двух младших байтах ( обратно в ноль). Это может быть использовано для удаления любых частей «другого целого числа» из той, которую вы сейчас извлекаете.

4 голосов
/ 22 октября 2012

Этот вопрос состоит из двух частей. Во-первых, как вы маскируете два 32-разрядных целых числа в 64-разрядное длинное целое число?

Как уже говорили другие, допустим, у меня есть функция, которая принимает координаты X и Y и возвращает longint, представляющий линейное значение этой точки. Я склонен называть это линеаризацией двумерных данных:

public long asLong(int x, int y) {
    return ( ((long)x) << 32 ) | y;
}

public int getX(long location) {
    return (int)((location >> 32) & 0xFFFFFFFF);
}

public int getY(long location) {
    return (int)(location & 0xFFFFFFFF);
}

Простите, если я параноидален по поводу порядка операций, иногда другие операции более жадные, чем <<, что заставляет вещи перемещаться дальше, чем они должны. </p>

Почему это работает? Когда это может потерпеть неудачу? Удобно, что целые числа имеют тенденцию быть точно половиной размера longints. Что мы делаем, это приводим x к long, сдвигаем его влево до тех пор, пока он полностью не окажется слева от y, а затем выполняем операцию объединения (OR), чтобы объединить биты обоих.

Давайте представим, что это 4-разрядные числа, объединяемые в 8-разрядное число:

x = 14     :      1110
y =  5     :      0101

x = x << 4 : 1110 0000

p = x | y  : 1110 0000
           OR     0101
             ---------
             1110 0101

Между тем обратное:

p = 229    : 1110 0101  
x = p >> 4 : 1111 1110  //depending on your language and data type, sign extension
                        //can cause the bits to smear on the left side as they're
                        //shifted, as shown here. Doesn't happen in unsigned types
x = x & 0xF:
             1111 1110
         AND 0000 1111
         -------------
             0000 1110  //AND selects only the bits we have in common

y = p & 0xF:
             1110 0101
         AND 0000 1111
         -------------
             0000 0101  //AND strikes again

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

  • Это слишком много работы только для упаковки возвращаемого значения, которое почти всегда нужно немедленно распаковывать и читать вызывающей стороне. Это все равно, что вырыть дыру и заполнить ее.
  • Это значительно снижает читабельность вашего кода. "Какой тип возвращается?" Э-э ... инт ... и еще один инт ... в длинном.
  • Это может привести к появлению трудно отслеживаемых ошибок. Например, если вы используете неподписанные типы и игнорируете расширение знака, то позже переходите на платформу, которая заставляет эти типы дополнять два. Если вы сэкономите longint и попытаетесь прочитать его позже в другой части вашего кода, вы можете столкнуться с ошибкой off-by-one в битовом сдвиге и потратить час на отладку вашей функции только для того, чтобы узнать, что это неправильный параметр.

Если это так плохо, каковы альтернативы?

Вот почему люди спрашивают вас о вашем языке. В идеале, если вы находитесь в C или C ++, лучше сказать

struct Point { int x; int y; };

public Point getPosition() {
    struct Point result = { 14,5 };
    return result;
}

В противном случае в HLL, таких как Java, вы можете использовать внутренний класс для достижения той же функциональности:

public class Example {
    public class Point {
        public int x;
        public int y;
        public Point(int x, int y) { this.x=x; this.y=y; }
    }

    public Point getPosition() {
        return new Point(14,5);
    }
}

В этом случае getPosition возвращает Example.Point - если вы продолжаете часто использовать Point, продвигайте его до полного собственного класса. На самом деле, в java.awt уже есть несколько классов Point, включая Point и Point.Float

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

Ссылку на кортеж c ++ stdlib можно найти по адресу http://www.cplusplus.com/reference/std/tuple/

2 голосов
/ 19 января 2011

Хорошо .. @Felice прав, но если они оба умещаются в 16 бит, есть способ:

output_int = (first_int << 16) | second_int
                               ^
                           means 'or'

чтобы упаковать их, и

first_int = output_int & 0xffff
second_int = (output int >> 16) & 0xffff
                                ^
                           means 'and'

чтобы извлечь их.

1 голос
/ 19 января 2011

Два целых числа не могут соответствовать одному целому числу, или, по крайней мере, вы не можете вернуть два исходных.
Но в любом случае, если два исходных целых числа ограничены определенным количеством битов, вы можете (в псевдокоде): Первое целое число ИЛИ с (Второе целое число SHIFTLEFT (nOfBits))

для получения двух целых чисел замаскируйте объединенное целое число двоичным числом, представленным nOfBitsOne, и вы получите первое целое число, затем ShiftRight by nOfBits объединенное целое число, и вы возвращаете второе.

0 голосов
/ 19 января 2011

Вы можете хранить 2 16-разрядных целых числа в пределах 32-разрядного целого числа. Первый из 16 первых битов и второй из последних 16 битов. Для получения и составления значения вы используете операторы shift.

...