Newsgroup: it.comp.lang.c
Titolo: Operatore <<

Facendo delle prove con gcc ho trovato alcuni risultati che non mi 
aspettavo:
0x01ULL << 65 = 2
0x01ULL << 64 = 1
0x01ULL << 63 = 9223372036854775808
Con ULL si definiscono costanti a 64 bit giusto?
Com'� che lo shift di 63 bit porta ad un numero non memorizzabile su 64 
bit? Mi aspettavo che venisse troncato il risultato in modo da ottenere 0.
Il C non prevede shift superiori a 63 (senza troncare il numero ai primi 
6 bit meno significativi)?

 6 bit meno significativi)?
Prova a fare (1<<50)<<50. In questo caso il risultato dovrebbe proprio 
essere zero. Ma se l'operando di destra dello shift � troppo grande, 
immagino che il comportamento sia dipendente dall'implementazione.
E.

 immagino che il comportamento sia dipendente dall'implementazione.
No, che io sappia e' proprio undefined behaviour. 
In particolare lo standard dice 
"""
If the value of the right operand is negative or is greater than or
equal to the width of the promoted left operand, the behavior is
undefined.
"""
Quello che dici tu di fare ((0x01UL <<50) << 50) fa 0.
Di fatto da standard stai facendo:
((0x01UL * 2^50 mod (2^64-1)) * 2^50) mod (2^64-1)
In effetti invece usando 1 intero con segno sei ancora una volta
undefined behaviour, perche' e' richiesto che il risultato sia
rappresentabile nel tipo (cosa che non e'). Immagino che tu pero'
intendessi proprio ((0x01UL <<50) << 50) e non ((1 <<50) << 50).
-- 
-riko

 intendessi proprio ((0x01UL <<50) << 50) e non ((1 <<50) << 50).
1<<50 non dovrebbe dare problemi col segno, visto che 50<63. O forse non 
ho capito quello che dici.
E.

 ho capito quello che dici.
1<<50 non da problemi.
(1<<50)<<50 
si.
Il punto e' che 1 non e' unsigned, per C89 e nemmeno per C99.
Questo vuole dire che e' un int normalissimo. Non a caso, c'e' un bel
warning su 1<<50
% cat prova.c
#include <stdio.h>
int main() {
    if((1<<50) > 0)
        puts("Positive");
    else
        puts("Negative");
}
% cc -W -Wall -std=c99 prova.c -o prova
prova.c: In function 'main':
prova.c:4: warning: left shift count >= width of type.
Ovviamente con (1<<31) stampa "Negative", precisamente perche' e' un
intero *con* segno. Quindi overflow da proprio undefined behaviour.
Ovviamente con 1u (o anche 01 o 0x1 --iirc, sono sicuro pero' di 01 --
va tutto bene).
-- 
-riko

 ho capito quello che dici.
1<<50 da problemi su molte implementazioni.
(1<<50)<<50 
dovrebbe darli su sostanzialmente tutte.


 Il punto e' che 1 non e' unsigned, per C89 e nemmeno per C99.
Volevi dire che non ï¿œ long long! Altrimenti ancora non capisco quello 
che dici. Io sto dicendo che nell'aritmetica a 64 bits l'operazione 
1<<50 non dovrebbe dare problemi di segno. Solo che di solito 1 ha 32 
bits e per questo ottieni un errore nel tuo test. Immagino che 1LL<<50 
non dovrebbe dare problemi.
E.

 non dovrebbe dare problemi.
Allora "1" in C e' un int. Su *molte* piattaforme questo e' un problema,
perche' gli int *possono* avere meno di 64 bit e in particolare meno di
51bit.
Questo vuole dire che su tutte queste piattaforme e' un problema
scriverlo perche' sei automaticamente in undefined behaviour.
*Se* supporti gli int a 64 bit, *allora* (1 << 50) e' OK. Tuttavia
((1<<50)<<50) *non* lo e'. 
Viceversa, se scrivi 1u *e* hai una piattaforma dove un int e' piu' di
50 bit allora puoi scrivere sia 1<<50 che ((1<<50)<<50).
(1LL<<50) *non* da problemi, ma ((1LL<<50)<<50) ancora una volta li da.
A meno che non sei su una piattaforma con i LL enormi. Che io sappia C99
chiede almeno 64bit, ma di per se accetterebbe anche 128bit.
-- 
-riko

 ((1<<50)<<50) *non* lo e'. 
Non sono in grado di consultare lo standard. Per� ho sempre pensato che 
x<<1 sia valido qualunque sia x. Se hai gli interi a 32 bit (1<<50) 
dovrebbe essere garantito 0. Se hai gli interi a 64 bit (1<<50) == 0 e 
(1<<50)<<50 == 0.
E.

 (1<<50)<<50 == 0.
Io lo ho sotto il naso e te lo sto citando da due post. :)
(1<<50) con interi a 32 bit *non* e' garantito 0. 
E' *undefined behaviour*
((1<<50) << 50) con gli interi a 64 bit e' ancora una volta unsigned
behaviour.
Se invece usi 1u vanno entrambi bene (a patto di avere rispettivamente
le parole di 32bit e di 64 bit).
Il punto e' che l'aritmetica a intera con segno e senza segno e'
pesantemente diversa dal punto di vista della correttezza: di base
quando sei senza segno puoi "fare praticamente tutto" e il risultato che
ti aspetti dalla macchina e' quello corretto da standard (per esempio in
presenza di overflow). 
Viceversa quando hai il segno, hai in generale undefined behaviour. 
In questo caso non c'e' eccezione. Se continui a shiftare un unsigned
ottieni 0. Se continui a shiftare un signed, ottieni undefined
behaviour.
In particolare per valori "grandi" di x, se x e' signed, x << 1 ti da
undefined behaviour. Viceversa, se x e' unsigned ti da proprio 0.
-- 
-riko

 undefined behaviour. Viceversa, se x e' unsigned ti da proprio 0.
Questo non segue da quello che hai citato, ma comunque � sensato e mi 
fido che sia effettivamente cos�...
E.

 fido che sia effettivamente cos�...
Ne ho parlato immediatamente dopo la citazione. Non con un'altra
citazione diretta, ma semplicemente parafrasando: quando ho detto che il
valore dello shift e' richiesto essere rappresentabile nel tipo quando
si lavora con il segno. C'e' un passo preciso che lo dice.
-- 
-riko