Побитовый - или, кажется, имеет неявное приведение к long. Можно ли этого избежать? - PullRequest
1 голос
/ 15 января 2010

Я программист на Java, пытаюсь перейти на C #, и этот вопрос меня слегка озадачил:

int a = 1;

a = 0x08000000 | a;
a = 0x80000000 | a;

Первая строка компилируется просто отлично. Второго нет. Кажется, признается, что существует константа со знаковым битом, и по какой-то причине она решает преобразовать результат в long, что приводит к ошибке:

Невозможно неявно преобразовать тип 'long' в 'int'.
Существует явное преобразование (вам не хватает приведения?)

У меня есть исправление:

a = (int)(0x80000000 | a);

Который имеет дело с актерами, но все равно оставляет предупреждение:

Битовый оператор или оператор, используемый в операнде с расширенным знаком;
сначала рассмотрите приведение к меньшему типу без знака

Каким будет правильный способ C #, чтобы выразить это в виде ошибки / предупреждения / длинного свободного пути?

Ответы [ 6 ]

12 голосов
/ 15 января 2010

Мне интересно, что во всех этих ответах только один человек фактически предложил делать то, что говорится в предупреждении . Предупреждение говорит вам, как решить проблему; обратите на это внимание.

Битовый или оператор, используемый в операнде с расширенным знаком; сначала рассмотрите приведение к меньшему типу без знака

Битовый или оператор используется в операнде с расширенным знаком: int. Это приводит к преобразованию результата в более крупный тип: long. Беззнаковый тип, меньший чем long, является uint. Так что делай, что говорится в предупреждении; приведите операнд с расширенным знаком - int - к uint:

result = (int)(0x80000000 | (uint) operand);

Теперь нет расширения знака.

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

12 голосов
/ 15 января 2010

Числовой целочисленный литерал по умолчанию равен int, если только число не слишком велико, чтобы поместиться в int, и вместо этого оно становится uint (и т. Д. Для long и ulong).

Поскольку значение 0x80000000 слишком велико, чтобы поместиться в int, это значение uint. Когда вы используете оператор | на int и uint, оба расширяются до long, поскольку ни один из них не может быть безопасно преобразован в другой.

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

a = unchecked((int)0x80000000) | a;

(Примечание. Это только указывает компилятору, как преобразовать значение, поэтому для преобразования в int не создано никакого кода.)

1 голос
/ 15 января 2010

Изменение этой строки на
(int)((uint)0x80000000 | (uint)a);
делает это для меня.

1 голос
/ 15 января 2010

Ваша проблема в том, что 0x80000000 является минусом в форме int, и вы не можете выполнять побитовые операции над минус-значениями.

Должно работать нормально, если вы используете uint.

a = ((uint)0x80000000) | a;  //assuming a is a uint
0 голосов
/ 30 июля 2012

Вы можете сделать его менее уродливым, создав константу, начинающуюся с буквы «О». (И в отличие от этого веб-сайта в визуальной студии он не отображается другим цветом).

const int Ox80000000 = unchecked((int)0x80000000);

/// ...

const int every /*  */ = Ox80000000;
const int thing /*  */ = 0x40000000;
const int lines /*  */ = 0x20000000;
const int up /*     */ = 0x10000000;
0 голосов
/ 15 января 2010

Проблема у вас здесь в том, что

  • 0x80000000 - это целое число без знака . В спецификации говорится, что целочисленный литерал имеет первый тип в списке (int, uint, long, ulong), который может содержать литерал. В данном случае это uint.
  • a это вероятно и int

В результате результат будет long. Я не вижу ничего лучшего, кроме как привести результат обратно к int, если только вы не знаете, что a не может быть отрицательным. Затем вы можете разыграть a до uint или объявить это таким образом.

...