1

Тема: Від коли це (1 >> 32) == 1 ?

Написав код, стартував тести, а вони всі провалені (failed). Почав debug, і отримав не очікувані значеня починаючи з першого рядка мого коду. Докопав до причини, виявляється (1 >> 32) дає не 0 (в C#).

(1 >> 32) == 1    // ну добре, може десь це має сенс,
(4 >> 32) == 4    // якщо представляти послідовність бітів циклом.
(4 >> 33) == 2    // те саме.
(4 >> 31) == 0    // але тоді як це розуміти ?

2

Re: Від коли це (1 >> 32) == 1 ?

Підозрюю там 1, бо вираз перетворюється в (1 >> 0) == 1. А чого ви очікували?

Подякували: ReAl, leofun012

3

Re: Від коли це (1 >> 32) == 1 ?

Знайшов в доках опис того як працює operator >> :

microsoft.com написав:
  • Якщо тип x є int або uint, зміщеня визначають 5 наймолодших бітів правого операнда. Тобто результат зміщеня такий як з count & 0x1F (або count & 0b_1_1111).

  • Якщо тип x є long або ulong, зміщеня визначають 6 наймолодших бітів правого операнда. Тобто результат зміщеня такий як з count & 0x3F (або count & 0b_11_1111).

.. що ж .. це повністю ламає основну логіку (з переповненям) і ламає логіку з циклами.

Подякували: koala1

4

Re: Від коли це (1 >> 32) == 1 ?

wander написав:

А чого ви очікували?

Початково я очікував (1 >> 32) == 0. Мені це видається достатньо простим в реалізації (на рівні заліза), щоб очікувати на такий результат. Але комусь (серед мікромяких) це видалось чимось зайвим.

5

Re: Від коли це (1 >> 32) == 1 ?

leofun01 написав:

це повністю ламає основну логіку (з переповненям)

Ви що хочете ще одну вері-вері ансейф лангуаге як С/С++? :D
Тут завбачливо подумали за вас.

leofun01 написав:

Початково я очікував (1 >> 32) == 0.

Швидше було б (1 >> 32) == UB.

leofun01 написав:

Але комусь (серед мікромяких) це видалось чимось зайвим.

Це не вони, в джаві те ж саме (вроді).

Подякували: ReAl, leofun012

6

Re: Від коли це (1 >> 32) == 1 ?

1>>32 з погляду безпеки коду має бути ArgumentException, бо бітів не вистачає, а отже, програма, цілком можливо, робить щось не те, що планував програміст.

Подякували: leofun011

7

Re: Від коли це (1 >> 32) == 1 ?

wander написав:

Тут завбачливо подумали за вас.

Я би зказав, вони завбачливо подумали за нікого, а варто було подумати як мінімум за себе, тоді розробникам не довелось би думати замість них.

wander написав:
leofun01 написав:

Початково я очікував (1 >> 32) == 0.

Швидше було б (1 >> 32) == UB.

От халепа, дійсно, на рівні заліза ця штука залежить від моделі процесора.

koala написав:

1>>32 з погляду безпеки коду має бути ArgumentException, бо бітів не вистачає, а отже, програма, цілком можливо, робить щось не те, що планував програміст.

або так.

8

Re: Від коли це (1 >> 32) == 1 ?

koala написав:

1>>32 з погляду безпеки коду має бути ArgumentException

Мені пояснили, що кидати Exception'и на >> (для стандартних числових типів) взагалі було не варіант, бо тоді на кожний виклик такого оператора було би +1 cmp, +1 jmp (тобто branching), і в одній з гілок висів би object.ctor(), який всюди (в стандартних бібліотеках) довелось би ловити. Цього хотіли уникнути щоб не завалити перформанс. Тому зробили обрізаня до молодших бітів (+1 and).

9

Re: Від коли це (1 >> 32) == 1 ?

Ну це ж вічний вибір: або повільно і безпечно, або швидко і ризик UB, або і трохи повільніше, ніж написано, і поведінка дивна, зате передбачувана.
Я чомусь думав, що в C# вибір завжди на користь безпеки. Ну що ж, помилявся.

Подякували: leofun01, Tarpan872

10 Востаннє редагувалося ReAl (01.05.2024 17:48:00)

Re: Від коли це (1 >> 32) == 1 ?

leofun01 написав:

Знайшов в доках опис того як працює operator >> :

microsoft.com написав:
  • Якщо тип x є int або uint, зміщеня визначають 5 наймолодших бітів правого операнда. Тобто результат зміщеня такий як з count & 0x1F (або count & 0b_1_1111).

  • Якщо тип x є long або ulong, зміщеня визначають 6 наймолодших бітів правого операнда. Тобто результат зміщеня такий як з count & 0x3F (або count & 0b_11_1111).

.. що ж .. це повністю ламає основну логіку (з переповненям) і ламає логіку з циклами.

Це все сховане в глибинах архітектури.
Ну а також в компіляторі та увімкненій оптимізації — буде залежність від того, "32" прийшло у вираз як константа/змінна з "відомим" компілятору станом, чи через змінну якимсь способом, який компілятор не може проаналізувати, тобто замість асемблерної команди з константою для числа зсуву буде команда з числом у регістрі (*CX для x86*).
Для швидкого (однотактового) зсуву на довільне число в процесорах перед/у АЛП стоїть так званиий barrel shifter, що є оптимізованою групою мультиплексорів, які підключають біти виходу до потрібних бітів входу. Відповідно, 32-входові мультиплексори мають 5-входову адресу, на яку подаються 5 молодших бітів того регістра, який визначає зсув, а всі інші біти ігноруються. Ну або 5 бітів відповідного поля команди, якщо компілятор уоптимізував код, але тоді при зсуві на-більше-ніж він якраз у 0 і уоптимізує. І результат залежатиме від опцій оптимізації.

Нещодавно на роботі довелося все це пояснювати, коли у колег виникло запитання, чому MISRA лається на

#define MASK(n)  ((1U << (n)) - 1U)

коли n прийшло як аргумент функції (а там де константа, там ніколи в коді 32 не було).
Навіть довелося код накатати, де у циклі виводилися результати двох зсувів, на некваліфікований лічильник циклу і на його копію у volatile-змінній.

Подякували: wander, Chemist-i, leofun013