Newsgroup: it.comp.lang.c
Titolo: segno negativo non troncato con unsigned -C

unsigned int x=-8;
printf("%d",x);
 
x, ï¿œ negativo (-8) e mi aspettavo che mi stampasse 8 con unsigned.
 
Perchï¿œ non mi tronca il segno meno?
 
grazie
 
nero23

 Perchè non mi tronca il segno meno?
 
Perché hai detto di stampare un valore intero con segno.
 
 


Correggetemi se sbaglio, ma giurerei che con gli *unsigned* non è 
imprevedibile -- con i signed invece sono sicuro di sì.


 
 
Il problema è appunto che quel "-8" è un int, e lo stai assegnando ad un
unsigned! ;)
Ciao!


In un linguaggio di programmazione di alto livello un assegnamento di 
questo tipo sarebbe un errore o warning a tempo di compilazione e a 
runtime un risultato imprevedibile in quanto l' assegnamento è fuori dal 
dominio del problema.
In C ansi questo caso specifico non è cosi
Lo standard dice chiaramente questo:
 
  When a signed integer is converted to an unsigned integer of equal or 
greater size, and the signed integer value is nonnegative, its value is 
unchanged. If the signed integer value is negative, then:
 
     If the unsigned integer type is larger, the signed integer is first 
promoted to the signed integer that corresponds to the unsigned integer; 
then the value is converted to unsigned by adding to it one greater than 
the largest number that can be represented in the unsigned integer type.
 
     If the unsigned integer type is equal or smaller than the signed 
integer type, then the value is converted to unsigned by adding to it 
one greater than the largest number that can be represented in the 
unsigned integer type.
 
 
 
 
 
 
 

 imprevedibile -- con i signed invece sono sicuro di sì.
 
"Imprevedibile" non vuol dire "non definito". Gli standard definiscono 
per filo e per segno quali valori puoi ottenere dalla conversione, quale
valore otterrai dipende da molti fattori casuali.
 
Vedi risposta a enoquick

 ...
 
Cioè lo standard ha delle regole precise per la conversione di tipi,
però tu non puoi sapere  se hai interi a sedici bit, a trentadue bit
o sarcazzo per cui non sai se -8 sia uguale a  0XF8, 0XFFF8, 
0XFFFFFFF8 o sarcazzo.
 

 0XFFFFFFF8 o sarcazzo.
 
Giusto, fino ad un certo punto.
Proprio per i passi citati, se provi a convertire un int32_t ad un uint32_t
sai cosa succederà (parlando di C99, ovviamente)
In ogni caso concordo con il concetto di fondo: se ti serve un unsigned usi un
unsigned, altrimenti no, pena: ogni conversione va fatta pensando a quello che
potrebbe succedere nel peggiore dei casi.
Ciao!


 
Lo standard non parla, giustamente, di numero di bit
 
Ma dice quale deve essere il comportamento in caso di troncamento o no
Infatti aggiunge anche questo:



printf è una funzione di libreria con parametri dal 2^ in poi generici. 
Il tipo dei parametri passati li valuta dal primo argomento (%d 
significa intero con segno) quindi per lei x è un signed int
 
 
 
 
 

 quello che potrebbe succedere nel peggiore dei casi.
 
Attenzione però. 
 
unsigned int  x=-8;
 
dichiara una variabile e la inizializza con un ben precisa valore 
binario: ultimi tre bit saranno zero, tutti gli altri saranno uno.
Due cose sono non specificate, la lunghezza, per cui tu non sai se
la variabile sarà 0XF8, 0XFFF8, 0XFFFFFFF9, ... e la endianness per
cui tu non sai se le variabile sarà memorizzata come 
0XF8 0XFF 0XFF 0XFF oppure 0XFF 0XF0 0XFF 0XFF oppure
0XFF 0XFF 0XFF 0XF8.
 
Ma fai attenzione, l'incertezza ed il pericolo ci sono nelle 
situazioni  in cui la taglia e la endianness hanno importanza, se
tu vuoi solo che gli ultimi tre bit siano zero e gli altri uno
indipendentemente  dalla grandezza non vedo cosa ci sia di male in:
 
unsigned int  x=-8;
 

 ...
 
Come ho già accennato in un'altro articolo, i problemi nascono quando
ti aspetti una certa cosa (p.e. interi a 32 bit su una macchina little
endian) ma ne hai un'altra (p.e. interi a 16 bit su una macchina big
endian), questo non perché gli standard del C non siano chiari o 
imprecisi o aleatori, ma perché i programmi (e i programmatori) 
viaggiano da una macchina ad un'altra.
 
 

 mentre in C è tutto ben documentato e razionale?
 
Se tutto è documentato e razionale non lo so
Fatto sta che in C ansi hanno previsto alcuni comportamenti che devono 
avere dei compilatori invece che lasciarli semplicemente indefiniti o 
implementation defined.
Un esempio è l' assegnamento di un signed in un unsigned.
 
 
 

 assegnamento.
 
A suo tempo?  Alle nove  e mezzo di sera? L'articolo dell'OP 
è stato postato nella pausa pranzo.

 alto livello ai giorni nostri, è piu un assembler portabile)
 
Vabbè, Dedicati ad ADA e lascia stare il  allora. 
 

 Se non lo capisci rileggiti quello ho postato prima e la tua risposta.
 
Sinceramente stiamo esagerando. senza polemiche, non voglio farne
nè era mia intenzione farle. Stiamo discutendo di cose assolutamente 
irrilevanti. La mia intenzione era solo dire che discutere sul warning, 
su errori a tempo di compilazione e simili sono questioni di lana 
caprina, anzi ancora meno importante: la lana caprina, il cosidetto
cachemire, è una buona lana. abbiamo perso di vista il tema:
 
unsigned int x=-8;
printf("%d",x);
 
La printf stampa un intero con segno, deve essere:
 
printf("%ud",x);
 
Quanto a:
 
unsigned int x=-8;
 
è una istruzione perfettamente lecita in C ma è pericolosa se non 
si sa esattamente ciò che si fa, come tutto in C.

 si sa esattamente ci� che si fa, come tutto in C.
 
veramente si sa esattamente cosa fa, se si conosce il compilatore.
 
Ciao Jack
-- 
Yoda of Borg am I! Assimilated shall you be! Futile resistance is, hmm?

 veramente si sa esattamente cosa fa, se si conosce il compilatore.
 
fatto bene a puntualizzare


E perchè ?
Non ho mai considerato il C un linguaggio di serie B
 


 
 
 
Fa piacere che affermi ciò, visto che avevi affermato che il risultato 
era imprevedibile
Come ha detto Jack nella sua risposta si sa cosa fa se si conosce il 
compilatore
 
 


A  suo tempo non significa immediatamente
 


Faccio reply qui ma è un'osservazione più generale.
Non capisco il perché di tutto questo discorso.
Per quanto mi riguarda:
se dichiaro una variabile unsigned devo assegnare un valore unsigned, punto.
Se questo, da qualche parte, non succede, c'è un errore logico nel codice, e
va sistemato! Questo considerando anche che, se voglio qualcosa di particolare,
ci sono funzioni specifiche per ottenere lo stesso risultato in maniera pulita,
comprensibile e portabile.
Conoscere quello che fa il proprio compilatore in quello specifico caso,
quindi, è inutile!
 
Ciao!


 
Il discorso č semplice
L' amico Neonano aveva affermato che assegnare un numero negativo ad un 
  unsigned era un risultato imprevedibile.
Ho fatto osservare che lo std prevede alcuni comportamenti da parte del 
compilatore  perfettamente prevedibili e questo era uno di quei casi, 
tutto qua.
Tu consideri assegnare un numero negativo ad unsigned una cosa da non 
fare, ma invece questo č un ragionamento che puň avere un senso in un 
linguaggio che č un assembler portabile e se ragioni a livello di bit e 
bytes
 
Es:
 
     unsigned mask  = -1;
 
Dal mio punto di vista crea una maschera di bit tutti a 1, perfettamente 
logico.
Ok, lo puoi fare anche cosi:
 
   unsigned mask = 0xFFFFFFFF;
 
Ma questo non č portabile perchč assumi che un unsigned sia di 4 bytes.
Ed ecco che assegnare -1 ad un unsigned č un modo per rendere il codice
indipendente dalla size dell' unsigned quindi portabile e che assume un 
ben preciso significato logico se si ragiona in termini di bit.
 
 
 
 
 
 
 
 
 
 
 
 

 
 
E' questione di gusti..
Quello che dici di solito si fa con -1 perché funziona sempre, mentre cose come:
unsigned long v = ~0u;
oppure
unsigned int v = ~0;
non funzionano sempre (il primo per il numero di bit (int può avere meno bit di
long), il secondo per la rappresentazione (non è detto che sia in complemento
a due)).
Sinceramente quando programmo codice che usa maschere (e quindi, di riflesso,
che mi aspetto sia portabile su più piattaforme "risicate") mi definisco in 
un .h i tipi e le maschere con dei typedef e delle define così come mi aspetto
per ogni macchina target (e il target va specificato in compilazione! ;)) e uso
quelle nel codice: in questo modo racchiudo la "logica macchina" in un file e
posso usare la "logica umana" (quella che descrivevo prima, unsigned con 
unsigned, signed con signed) in tutti gli altri moduli.
Ciao!


 
Si, in generale lo faccio anch'io, non credere che non definisca header 
di quel tipo.
Comunque se vuoi una maschera di tutti 1 senza usare -1 o ~0 c'è la 
libreria standard.
Le costanti ULONG_MAX e UINT_MAX sono lì apposta