inaspettato risultato conversione tipi

Ciao, ho appena preso arduino e mi stavo dilettando con i led.
(esiste un topic di presentazione?)

Scrivendo questo programmino

void loop()
{
 for(int i=0; i<=11;i++ ){
  Serial.print("value unsigned: ");
  Serial.print(((unsigned int)(BRIGHT*((sin((-pi/6)+i*pi/6))))));
    Serial.print("\tvalue signed: ");
  Serial.println(((int)(BRIGHT*((sin((-pi/6)+i*pi/6))))));
 }
}

Ho ricevuto come risposta

value unsigned: 0 	value signed: 0
value unsigned: 2046	value signed: 2046
value unsigned: 3545	value signed: 3545
value unsigned: 4094	value signed: 4094
value unsigned: 3548	value signed: 3548
value unsigned: 2052	value signed: 2052
value unsigned: 6	        value signed: 6
value unsigned: 0  	value signed: -2040
value unsigned: 0 	value signed: -3542
value unsigned: 0 	value signed: -4094
value unsigned: 0 	value signed: -3551
value unsigned: 0 	value signed: -2046
value unsigned: 0 	value signed: 0

Assegnando un valore negativo ad un unsigned int mi sarei aspettato un qualcosa del tipo:

the resulting value is the least unsigned integer congruent to the source integer (modulo 2^n where n is the number of bits used to represent the unsigned type)

Effettivamente i led si comportano in quel modo, rimanendo spenti. volevo capire come mai ottengo questo tipo di output.

Ciao

Anch' io mi aspettavo un valore diverso.

Confermo che se si eccede con un int oltre 32678 escono numeri negativi.

Qua deve risponderti qualcuno che ne sa di piú.

Ciao Uwe

giacomomae:
Assegnando un valore negativo ad un unsigned int mi sarei aspettato un qualcosa del tipo:

Il calcolo del seno rende un valore float, un float negativo è un valore che non può essere convertito tramite casting in unsigned int, questo tipo di operazione da sempre 0, mentre può essere convertito in signed int.
Solo se assegni ad un unsigned int il valore float negativo questo diventa il relativo complemento a due su 16 bit.

Questo semplice codice di test dimostra quanto esposto sopra:

float a;
unsigned int b;

void setup() {
Serial.begin(19200);
  
a = 55,12;
Serial.println ((unsigned int)a);
Serial.println ((int)a);  
a = -55,12;
Serial.println ((unsigned int)a);
Serial.println ((int)a);  
b = a;
Serial.println (b);   

}
void loop() {
 
}

Il risultato è:

55
55
0
-55
65481

astrobeed:
Il calcolo del seno rende un valore float, un float negativo è un valore che non può essere convertito tramite casting in unsigned int, questo tipo di operazione da sempre 0, mentre può essere convertito in signed int.
Solo se assegni ad un unsigned int il valore float negativo questo diventa il relativo complemento a due su 16 bit.

Grazie per la risposta!
Mi hai dato un ottimo spunto di riflessione :
mi sono andato a rileggere il K&R (non ci si impara mai abbastanza)

in appendice A.6.2 conversioni integrali ho trovato (quello che mi sarei aspettato io :slight_smile: ):

Un intero è convertito in un dato tipo senza segno individuando il più piccolo valore non negativo che sia congruo a quell'intero Modulo (MAX_VALUE+1)

ovvero quello che avviene nel tuo programmino quando fai

b = a

cioè fai una conversione float -> signed int -> unsigned int

pero' poco più sotto in A.6.3 si svela l'arcano! :

ciò che risulta dalla conversione di valori in virgola mobile negativi in tipi integrali privi di segno non è specificato

quindi la conversione negative float -> unsigned int
dipende dall'implementazione del compilatore!

Vorrei capire che cosa vi aspettavate dalla conversione di un numero negativo in uno senza segno.

zoomx:
Vorrei capire che cosa vi aspettavate dalla conversione di un numero negativo in uno senza segno.

In binario i numeri negativi sono rappresentati come 2^n - valore, ove n è il numero di bit significativi, se ragioniamo su otto bit -1 è pari a 2^8-1 = 256 - 1 = 255, il limite è dato dall'ultimo bit della word, se è ZERO il numero è positivo, se è UNO il numero è negativo.
Se assegni, o fai il cast, un numero negativo su una variabile di tipo unsigned ottieni il suo valore assoluto senza segno, pertanto -1 diventa 255 se sono unsigned char, 65535 se sono unsigned int.

x iscrizione.

astrobeed:
Se assegni, o fai il cast, un numero negativo su una variabile di tipo unsigned ottieni il suo valore assoluto senza segno, pertanto -1 diventa 255 se sono unsigned char, 65535 se sono unsigned int.

La mia domanda era cosa ci si aspetta dal punto di vista logico.
Secondo quanto scritto sopra

ciò che risulta dalla conversione di valori in virgola mobile negativi in tipi integrali privi di segno non è specificato

quindi usare il cast da risultati imprevedibili, nel senso che dipendono dal compilatore. Ma io chiedevo perché usare il cast, che a quanto pare da risultati imprevedibili, quando ci sono funzioni apposta che invece hanno un risultato identico (in ogni compilatore), tipo quelle che restituiscono il valore assoluto e la parte intera.

Non so se mi sono spiegato.

zoomx:
La mia domanda era cosa ci si aspetta dal punto di vista logico.
Secondo quanto scritto sopra

La tua domanda non ha alcun senso, i casi sono due o conosci la matematica binaria e il linguaggio, quindi sai cosa aspettarti, oppure non lo conosci e non sai cosa aspettarti, nel secondo caso basta studiare.
In tutti i casi il casting di un float negativo verso un valore unsigned produce sempre 0, è previsto esplicitamente nel C ANSI, il motivo è legato alla natura esponenziale dei float e per come vengono rappresentati in memoria, oltretutto non esiste un solo formato per i float.

questo tipo di operazione da sempre 0

astrobeed:
In tutti i casi il casting di un float negativo verso un valore unsigned produce sempre 0, è previsto esplicitamente nel C ANSI

sul kernigan, come già riportato precedentemente, ho trovato scritto (Par. A.6.3):

ciò che risulta dalla conversione di valori in virgola mobile negativi in tipi integrali privi di segno non è specificato

che mi sembra non sia perfettamente allineato con quello che stai dicendo,
mi potresti dare dei chiarimenti in merito?

quindi usare il cast da risultati imprevedibili, nel senso che dipendono dal compilatore.

no no i risultati sono prevedibilissimi.

E' la stessa cosa che succede utilizzando una arduino DUE o una UNO:
per l'arduino due un int è un'intero a 32 bit,
mentre per la UNO un int è a 16 bit :slight_smile:

ma non è che se fai 65535 * 2 il risultato è imprevedibile.
con la due hai come risultato 131070
con la uno -2

EDIT: modificato il risultato.
(-1 * 2 = -2 u_u )

Poco sopra quoti il K&R secondo cui ciò che risulta dalla conversione di valori in virgola mobile negativi in tipi integrali privi di segno non è specificato. Poi però più sotto scrivi che il risultato è prevedibilissimo.
Forse diamo un significato diverso alle parole "non è specificato".

Per la cronaca il risultato di questo

    Serial.print("intero ");
    intero=65535;
    intero=intero*2;
    Serial.print(intero);

su una UNO è -2 e non -1. Appena provato. Ho anche scoperto che l'emulatore di Codeblock invece stampa 131070 segno che gli interi non occupano 2 byte, come sulla UNO che deve emulare, ma 4.

Astrobeed,
la mia tesi è che il casting di un numero negativo in un unsigned non è un'operazione intelligente, ma forse mi sbaglio.

zoomx:
Poco sopra quoti il K&R secondo cui ciò che risulta dalla conversione di valori in virgola mobile negativi in tipi integrali privi di segno non è specificato. Poi però più sotto scrivi che il risultato è prevedibilissimo.
Forse diamo un significato diverso alle parole "non è specificato".

Per la cronaca il risultato di questo

    Serial.print("intero ");

intero=65535;
    intero=intero*2;
    Serial.print(intero);



su una UNO è -2 e non -1. Appena provato. Ho anche scoperto che l'emulatore di Codeblock invece stampa 131070 segno che gli interi non occupano 2 byte, come sulla UNO che deve emulare, ma 4.

Astrobeed,
la mia tesi è che il casting di un numero negativo in un unsigned non è un'operazione intelligente, ma forse mi sbaglio.

si si il risultato è -2
ho sbagliato a digitare!
:stuck_out_tongue: chiedo venia!

prevedibile una volta fissato il compilatore!
e infatti ho previsto l risultato sia per la uno che per la due :smiley:

giacomomae:
sul kernigan, come già riportato precedentemente, ho trovato scritto (Par. A.6.3):

Il K&R è un testo obsoleto, devi fare riferimento al C standard che tutti i compilatori implementano, ovvero il C ANSI, come minimo il C99 (stabilito nel 2000) ISO/IEC 9899:1999.
Lasciamo perdere i compilatori non ANSI C o dialetti vari del C, quelli non li prendo nemmeno in considerazione in quanto i problemi sono ben altri.

zoomx:
la mia tesi è che il casting di un numero negativo in un unsigned non è un'operazione intelligente, ma forse mi sbaglio.

Dipende da cosa devi fare, il casting è uno strumento comodo e potente, come i puntatori, però tocca saperlo usare altrimenti si va incontro a casini inenarrabili :slight_smile:

Non dico che il casting non serve, ci mancherebbe, solo che forse in alcune situazioni non ha senso.

Ho anche scoperto che questo

  Serial.println();
  Serial.print("intero ");
  intero=65535;
  intero=intero*2;
  Serial.println(intero);
  intero=intero/0;
  Serial.print(intero);

viene compilato senza problemi (IDE 1.0.6) e che il risultato della divisione per zero è uno. Codeblock invece mi da un warning (warning: division by zero [-Wdiv-by-zero]) e l'emulatore va in crash. A quanto ho capito il C non prevede affatto controlli del genere. Invece è pignolo per i punti e virgola mancanti (o è il preprocessore?).

La divisione esplicita per zero è ovviamente una operazione cretina.

zoomx:
viene compilato senza problemi (IDE 1.0.6) e che il risultato della divisione per zero è uno. Codeblock invece mi da un warning (warning: division by zero [-Wdiv-by-zero]) e l'emulatore va in crash. A quanto ho capito il C non prevede affatto controlli del genere. Invece è pignolo per i punti e virgola mancanti (o è il preprocessore?).

E' l'ide di Arduino che non ti fa vedere i warning, setta un livello molto basso, il C lo da eccome il warning per la divisione per zero, se poi fai girare il programma il crash è garantito, se provi a compilare con Atmel Studio e avr-gcc le vedi tutte le warning.
Figurati che nei micro di livello superiore, p.e. i dsPIC senza dover arrivare agli ARM, il compilatore C prevede degli appositi interrupt di trap nel caso si verificano condizioni critiche di errore a run time, tra cui il divide by zero.

Come ho scritto prima, mi aspettavo che l'Arduino andasse in crash invece ha stampato un uno.
Certo, se avesse stampato un 42.....

zoomx:
Certo, se avesse stampato un 42.....

Arduino non è "Pensiero Profondo" :grin: