Установка всех битов в диапазоне [m, n] на 1 [дублировать]

Сначала мы создаем контейнер .Box и устанавливаем ширину и высоту. Честно говоря, это просто для демо, я установил 80vw и 80vh. И этот контейнер должен быть гибким элементом с ориентацией столбцов flex-flow: column wrap;. И мы устанавливаем максимальную ширину наибольших блоков (.BoxItem--large { max-width: 80%; height: 100%; }) на 100% высоты родителей. Он предназначен для переноса других элементов на новую строку / столбец. Поэтому элементы из другого столбца должны заполнять все пространство, поэтому мы устанавливаем .BoxItem--small { max-width: 20%; height: auto; } - да, 80% + 20% == 100%, что математика. позволяет изменять поведение на экранах с низкой шириной. @media screen and (max-width: 600px) { .Box { flex-flow: column nowrap; } } нам не нужно обертывать элементы, поэтому просто измените это. После этого мы хотим, чтобы каждый элемент BoxItem унаследовал ширину родителей. .BoxItem { width: 100%; max-width: 100%; } max-width: 100%; для каскадирования, мы не хотим заботиться о некоторых старых правилах, таких как .BoxItem--large { max-width: 80%; } и .BoxItem--small { max-width: 20%; }. И установите некоторые изменения порядка.

*,
*:before,
*:after {
  box-sizing: inherit;
}
html {
  box-sizing: border-box;
}
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}
body {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
  -webkit-align-items: center;
      -ms-flex-align: center;
          align-items: center;
  -webkit-box-pack: center;
  -webkit-justify-content: center;
      -ms-flex-pack: center;
          justify-content: center;
  background-color: #F72F4E;
  overflow: hidden;
}


.Box {
  width: 80vw;
  height: 80vh;
  background-color: rgba(0,0,0,.2);
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  -webkit-flex-flow: column wrap;
      -ms-flex-flow: column wrap;
          flex-flow: column wrap;
  -webkit-box-align: baseline;
  -webkit-align-items: baseline;
      -ms-flex-align: baseline;
          align-items: baseline;
}

.BoxItem {
  background-color: #fff;
  border: 1px solid #000;
}
.BoxItem--large {
  max-width: 80%;
  height: 100%;
}
.BoxItem--small {
  max-width: 20%;
  height: auto;
}
@media screen and (max-width: 600px) {
  .Box {
    -webkit-flex-flow: column nowrap;
        -ms-flex-flow: column nowrap;
            flex-flow: column nowrap;
  }
  .BoxItem {
    width: 100%;
    max-width: 100%;
  }
  .BoxItem--large {
    height: auto;
  }
  .BoxItem--1 {
    -webkit-box-ordinal-group: 3;
    -webkit-order: 2;
        -ms-flex-order: 2;
            order: 2;
  }
  .BoxItem--2 {
    -webkit-box-ordinal-group: 2;
    -webkit-order: 1;
        -ms-flex-order: 1;
            order: 1;
  }
  .BoxItem--3 {
    -webkit-box-ordinal-group: 4;
    -webkit-order: 3;
        -ms-flex-order: 3;
            order: 3;
  }
}
<div class="Box">
  <div class="BoxItem BoxItem--large BoxItem--1">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Iusto omnis rerum ea assumenda velit. Dolores ea unde iste esse illo sit, repellat molestias deleniti, voluptas expedita commodi odio possimus amet?</div>
  <div class="BoxItem BoxItem--small BoxItem--2">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Iusto omnis rerum ea assumenda velit.</div>
  <div class="BoxItem BoxItem--small BoxItem--3">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Iusto omnis rerum ea assumenda velit.</div>
</div>

52
задан Toby Speight 3 August 2016 в 10:47
поделиться

9 ответов

Это вызвано комбинацией неопределенного поведения в C и фактом, что код, созданный для процессоров IA-32, имеет 5-битную маску, применяемую к счету сдвига. Это означает, что на процессорах IA-32 диапазон смещения рассчитывается только 0-31. 1

Из Язык программирования C 2

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

Из IA-32 Руководство разработчика программного обеспечения Intel Architecture 3

8086 не маскирует количество сдвигов. Однако все остальные процессоры IA-32 (начиная с процессора Intel 286) маскируют счетчик сдвига до 5 бит, что приводит к максимальному счету 31. Эта маскировка выполняется во всех режимах работы (включая режим виртуального 8086) до уменьшите максимальное время выполнения инструкций.


1 http://codeyarns.com/2004/12/20/c- shift-operator-mayhem /

2 A7.8 Операторы сдвига, Приложение A. Справочное руководство, Язык программирования C

3 SAL / SAR / SHL / SHR - Shift, глава 4. Справочник по набору инструкций, Руководство разработчика программного обеспечения для архитектуры Intel IA-32

33
ответ дан celavek 18 August 2018 в 20:46
поделиться

Он работает не так, как ожидалось, потому что вы ожидаете слишком многого.

В случае x86 аппаратное обеспечение не заботится о операциях смены, где счетчик больше, чем размер регистра (см., например, описание инструкции SHL в справочной документации x86 для объяснения).

Стандарт C ++ не хотел налагать дополнительные расходы, рассказывая, что делать в этих случаях потому что сгенерированный код был бы вынужден добавить дополнительные проверки и логику для каждого параметрического сдвига.

С помощью этой свободы разработчики компиляторов могут генерировать только одну инструкцию сборки без каких-либо тестов или ветвей.

Более «полезным» и «логическим» подходом было бы, например, иметь (x << y) эквивалент (x >> -y), а также обработку высоких счетчиков с логическим и последовательным поведением.

Однако это имело бы потребовалось гораздо более медленное обращение для смещения бит, поэтому выбор заключался в том, чтобы делать то, что делает аппаратное обеспечение, оставляя программистам необходимость писать

Учитывая, что в этих случаях разные аппаратные средства делают разные вещи, что стандарт говорит в основном: «Что бы ни случилось, когда вы делаете странные вещи, просто не вините C ++, это ваша вина» переведено в legalese.

9
ответ дан 6502 18 August 2018 в 20:46
поделиться
  • 1
    Более точно, по крайней мере, в случае более старых версий gcc, a & lt; b будет реализовано как эквивалентное & lt; (& amp; 31). Я действительно полагался на это поведение когда-то в игре, где & gt; & gt; & gt; 33 был быстрее и генерировал эквивалентный код для & gt; & gt; 1 (из-за некоторых деталей реализации процессора 80486). Конечно, в ретроспективе я понимаю, насколько глупо было это делать, но я был молод и неопытен, и я дал мне значительное увеличение скорости в часто проходящей внутренней петле. – fluffy 13 September 2011 в 18:11
  • 2
    @fluffy: Это не gcc, это процессор. Учитывая, что стандарт не требует какого-либо конкретного поведения, что ребята из gcc сделали только для того, чтобы использовать инструкцию ассемблера для перехода, не заботясь о том, что делать, если счетчик был слишком большим или отрицательным. Если вы проверите руководство, на которое я указал, вы увидите, что действительно семейство x86 использует низкие 5 бит счетчика, игнорируя остальные ... в своих словах «Счет маскируется до пяти бит, что ограничивает диапазон отсчетов до От 0 до 31 ". Стандарт хотел оставить эту точную свободу, так что сдвиг может стать только одной инструкцией asm. – 6502 13 September 2011 в 18:24
  • 3
    Да, но в этом случае речь идет о том, как код генерируется компилятором. Это был случай shr eax vs. shr eax,1, и тот факт, что gcc имел фасет дизайна, в котором shr eax,33 имел бы И непосредственный операнд с 31, чтобы убедиться, что он вписывается в 5-разрядный слот параметра в инструкции , Immediate-mode shr имеет величину сдвига, закодированную непосредственно в инструкции , по крайней мере на x86 и большинстве других архитектур CISC. т. е. вопрос о том, как компилятор выполняет маскировку. ПОДДЕРЖИВАЙТЕ ЦП. Это прекрасное различие, но важное. – fluffy 13 September 2011 в 23:18
  • 4
    @fluffy: причина, по которой стандарт не указывает на поведение, когда счетчик больше размера бита значения, заключается в том, чтобы избежать пессимизма производительности в случае параметрического сдвига. В случае постоянного сдвига (например, 33) компилятор может просто упростить до константы 0 без каких-либо пессимизма производительности. Это gcc решило сохранить единообразное поведение в параметрическом случае, это разумный выбор, но не единственный (я надеюсь, что хотя бы предупреждение было выпущено). Если вам нужен контроль над командой asm, сгенерированной просто использовать asm ... использование не заданного поведения ищет проблемы. – 6502 14 September 2011 в 07:03
  • 5
    В то время gcc не выдавал предупреждения, но это было в gcc 2.x. В наши дни он выдаёт предупреждение, поэтому я даже вспомнил, что делал это ужасное, так как я устал от старого кода ради ностальгии. :) И да, я полностью осознаю, что поведение было неуказанным и что полагаться на это - плохая идея. Я просто добавлял больше к вашему ответу об одном из конкретных способов, которыми он может себя вести, который отличается от того, как вы заявляли. Я должен был уточнить, что я использовал метод «немедленного режима» & gt; & gt; а не режим регистрации & gt; & gt; & gt; & gt; хоть. – fluffy 14 September 2011 в 16:47

Ответы Lindydancer и 6502 объясняют, почему (на некоторых машинах) это печатается 1 (хотя поведение операции не определено). Я добавляю детали, если они не очевидны.

Я предполагаю, что (как и я) вы запускаете программу на процессоре Intel. GCC генерирует эти инструкции по сборке для операции переключения:

movl $32, %ecx
sall %cl, %eax

В разделе sall и других операциях смены на стр. 624 в справочном руководстве [* g0] указано:

8086 не маскирует количество сдвигов. Тем не менее, все остальные процессоры Intel Architecture (начиная с процессора Intel 286) маскируют счетчик сдвига до пяти бит, что приводит к максимальному счету 31. Эта маскировка выполняется во всех режимах работы (включая режим виртуального 8086), чтобы уменьшить Максимальное время выполнения команд.

Поскольку нижние 5 бит 32 равны нулю, тогда 1 << 32 эквивалентен 1 << 0, который является 1.

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

cout << (a << 32) << " " << (a << 33) << " " << (a << 34) << "\n";

будет печатать 1 2 4, и действительно, это то, что происходит на моей машине.

12
ответ дан antonakos 18 August 2018 в 20:46
поделиться

Поскольку вы смещаете бит int на 32 бита; вы получите: warning C4293: '<<' : shift count negative or too big, undefined behavior в VS. Это означает, что вы смещаетесь за пределы целого числа, и ответ может быть НИЧЕГО, потому что это неопределенное поведение.

5
ответ дан Bob2Chiv 18 August 2018 в 20:46
поделиться
  • 1
    Это не означает, что ответ может быть 0 или 1, это означает, что может быть абсолютно что угодно – Flexo♦ 13 September 2011 в 13:48
  • 2
    Очевидно, вы правы. Ред. – Bob2Chiv 13 September 2011 в 14:01

Смещение 32-битной переменной на 32 или более бит является неопределенным поведением и может заставить компилятор заставить демонов вылететь из вашего носа.

Серьезно, большую часть времени вывод будет 0 (если int составляет 32 бит или меньше), так как вы перемещаете 1 до тех пор, пока он не опустится снова, и останется только 0. Но компилятор может оптимизировать его, чтобы делать все, что ему нравится.

См. Замечательную запись в блоге LLVM Что должен знать каждый программист C о неопределенном поведении , обязательный для каждого разработчика C .

8
ответ дан DarkDust 18 August 2018 в 20:46
поделиться
  • 1
    ИМО это не компилятор в этом случае, это аппаратное обеспечение, которое не обрабатывает сдвиги больше, чем регистры. Стандарт просто не хотел указывать то, что требовало бы более толстый и медленный код для всех параметрических сдвигов на некоторых ЦП. – 6502 13 September 2011 в 13:51

Это неопределенное поведение в соответствии со стандартом C ++:

Значение E1 & lt; E2 - левые сдвинутые позиции E2; освобожденные биты заполняются нулями. Если E1 имеет неподписанный тип, значение результата E1 × 2 ^ E2, приведенное по модулю больше, чем максимальное значение, представляемое в типе результата. В противном случае, если E1 имеет подписанный тип и неотрицательное значение, а E1 × 2 ^ E2 представляется в типе результата, то это результирующее значение; в противном случае поведение не определено.

19
ответ дан Gassa 18 August 2018 в 20:46
поделиться
  • 1
    это какой-то быстрый быстрый поиск! – Flexo♦ 13 September 2011 в 13:39
  • 2
    @awoodland Да, Google серьезно впечатляет! – David Heffernan 13 September 2011 в 13:39
  • 3
    @AnkitSablok Неопределенное поведение означает, что любое поведение может случиться;) check: stackoverflow.com/questions/2397984/… – AraK 13 September 2011 в 13:42
  • 4
    @Ankit Ответ не должен быть 0. Ответ не определен. Ответ может быть любым. В этом суть неопределенного поведения. – David Heffernan 13 September 2011 в 13:44
  • 5
    @AnkitSablok - это оправдывает поведение. Дело в том, что когда поведение не определено, компилятору разрешено делать все, что угодно. В лучшем случае угадайте, что компилятор просто решил ничего не делать (что совершенно верно в соответствии со стандартом) – VolatileStorm 13 September 2011 в 13:45

В C ++ сдвиг только четко определен, если вы смещаете значение меньше шагов, чем размер этого типа. Если int - 32 бита, то определено только 0 до 31 и включает в себя 31 шаг.

Итак, почему это?

Если вы посмотрите на базовое оборудование, которое выполняет сдвиг, если оно должно смотреть только на младшие пять бит значения (в 32-битном случае), оно может быть реализовано с использованием менее логических ворот, чем если бы он должен был проверять каждый бит значения.

Ответ на вопрос в комментарии

C и C ++ предназначены для работы как можно быстрее на любом доступном оборудовании. Сегодня сгенерированный код - это просто команда «сдвиг», независимо от того, как базовое оборудование обрабатывает значения за пределами указанного диапазона. Если бы языки указали, как должен вести себя сдвиг, сгенерированный мог бы проверить, находится ли счет сдвига в диапазоне до выполнения сдвига. Как правило, это дает три инструкции (сравнение, ветвь, сдвиг). (По общему признанию, в этом случае это не было бы необходимо, поскольку количество сдвигов известно.)

42
ответ дан Lindydancer 18 August 2018 в 20:46
поделиться
  • 1
  • 2
    @Beta - это не определено, чтобы не обременять реализации с бременем эмуляции в программном обеспечении, что было «нормой». в аппаратном обеспечении в момент времени, когда был задуман стандарт. – Flexo♦ 13 September 2011 в 13:46
  • 3
    @Beta: интересный вопрос! Большинство этих неопределенных операций отображаются непосредственно в инструкции процессора. Поведение, как правило, undefined , поскольку в противном случае потребуются проверки перед операциями, которые не будут эффективно реализованы на большинстве архитектур. Типичная безопасность / скорость. – Matthieu M. 13 September 2011 в 13:49
  • 4
    @Beta: «Не платите за то, что вам не нужно». принцип. Вы можете сделать чек, если ваш код ему нужен. Но если проверка была автоматической, вы не могли not сделать это. – MSalters 13 September 2011 в 14:53
  • 5
    @Beta, Представьте, что инструкция для переключения на определенном компьютере занимает 5 бит. Независимо от того, что вы делаете, вы просто не можете сдвинуть переменную на 32! (Что-то вроде 01011 (сдвиг) 010 (для регистрации 2) 110 (из регистра 6) ????? (сумма сдвига) (Это мнимая, но очень возможная инструкция)) – Shahbaz 14 October 2011 в 13:39

У меня была та же проблема, и это сработало для меня:

f = ((long long) 1 & lt; (i-1));

Где я могу любое целое число больше 32 бит. 1 должен быть 64-битным целым для перехода на работу.

0
ответ дан Pieter888 18 August 2018 в 20:46
поделиться

Вы можете попробовать следующее. Это фактически дает выход как 0 после 32 сдвигов влево.

#include<iostream>
#include<cstdio>

using namespace std;

int main()
{
  int a = 1;
  a <<= 31;
  cout << (a <<= 1);
  return 0;
}
0
ответ дан Sandip Agarwal 18 August 2018 в 20:46
поделиться
  • 1
    Это не объясняет почему существует разница между сдвигом 32 места и двумя сменами, которые составляют до 32 мест. – Toby Speight 3 August 2016 в 10:50
Другие вопросы по тегам:

Похожие вопросы: