Vai al contenuto
PLC Forum


Conversione Ieee754 -> Real


olaffo

Messaggi consigliati

Salve a tutti,

devo realizzare un blocco che converta una lettura di un valore codificato in IEEE-754, in formato Real.

Ho realizzato già un blocco che faccia tale conversione ma non sembra funzionare bene.

Qualcuno ha già realizzato una funzione del genere o può darmi una mano?

Inserisco il codice che ho realizzato io per eventuali commenti e correzioni:


ByteBasso2 := AND (Lettura[2], 127);

Mantissa := (int_to_dint(Lettura[1]) + (65536*int_to_dint(ByteBasso2)));

RotazExp := ROR (lettura[2], 7);

Esponente := int_to_real(AND (RotazExp, 255 ));

1_Mantissa := 1.0 + (0.108+((10.0**-7) * (dint_to_real(in := mantissa))));

if lettura[2].15 then

segno := 1;

else

segno := 0;

end_if;

valore := ((-1.0)**segno) * (2.0**(Esponente-127.0)) * 1_mantissa;

mantissa_1 := Lettura[1];

mantissa_2 := ByteBasso2;

exp := AND (RotazExp, 255 );

segno_o := segno;

L'ingresso Lettura[] è un array di 2 byte.

grazie

Link al commento
Condividi su altri siti


Non capisco cosa vuoi fare.

Se hai un numero codificato in IEEE 754 significa che hai un numero in formato REAL.

Quindi, io capisco che vorresti convertire un numero da REAL a REAL ???

Poi dici che la variabile "Lettura[]" è un array di due byte. Ma allora non è una variabile REAL.

Sinceramente, non ho proprio capito cosa tu voglia convertire.

Link al commento
Condividi su altri siti

Dal manuale dello strumento (CPT-DIN CarloGavazzi)

The variables are represented by 32-bit floating-point numbers in standard IEEE-754...

The representation of a 32-bit floating-point number as an integer is:

Bits

31 30 … 23 22 … 0

sign Exponent Mantissa

The value of the number is:

(-1)^sign * 2^(Exponent-127 ) *1.Mantissa

La comunicazione è su modbus RS485. Usando "Readvar" estraggo l'informazione del parametro su due word (mw) da queste devo ottenere il valore real.

Link al commento
Condividi su altri siti

Ma da quanto si capisce, il valore che leggi è già in formato REAL. Non devi fare nessuna conversione.

Devi solo interpretare le due variabili consecutive da 16 bit come una sola variabile da 32 bit che è già in formato REAL.

Al massimo, dovrai scambiare tra loro le due variabili o i 4 byte, ma non credo.

Link al commento
Condividi su altri siti

  • 1 month later...

Il formato con cui Ti viene restituito il valore (real 32 bits) dovrebbe essere disponibile su 2 registri di tipo INT (direttamente dal buffer di ricezione della funzione READ_VAR.

Il primo registro INT sarà relativo alla parte bassa e il ssecondo alla parte alta del valore letto come REAL.

Dovresti convertire tali registri da tipo INT in WORD, ad esempio:

val_basso:=INT_TO_WORD(buffer_ric[0])

val_alto:=INT_TO_WORD(buffer_ric[1])

E in seguito unire i 2 registri di tipo WORD in un registro REAL, ad esempio:

val_flottante:=WORD_AS_REAL(val_basso, val_alto).

Prova così dovrebbe funzionare.

Bye

Link al commento
Condividi su altri siti

  • 2 months later...

si il modbus TCP IP della NOE ti restituisce solamente BOOL INT o DINT devi convertirli sia per la lettura che per la lettura, devi appunto ricavarti il segno poi l'esponente e poi la mantissa dopo di che moltiplichi tutto con (-1)^sign * 2^(Exponent-127 ) *1.Mantissa ed è semplicemente quello che fanno i plc tutte le volte che dichiari un real..attento che riprodurlo con un pc a 64 Bit ti porta a fare errori di arrotondamento per cui devi essere furbo nelle conversioni.

Link al commento
Condividi su altri siti

Dovresti convertire tali registri da tipo INT in WORD, ad esempio:

val_basso:=INT_TO_WORD(buffer_ric[0])

val_alto:=INT_TO_WORD(buffer_ric[1])

E in seguito unire i 2 registri di tipo WORD in un registro REAL, ad esempio:

val_flottante:=WORD_AS_REAL(val_basso, val_alto).

si il modbus TCP IP della NOE ti restituisce solamente BOOL INT o DINT devi convertirli sia per la lettura che per la lettura

Perché siete tutti così smaniosi di complicarvi la vita inutilmente?

Se devo leggere una variabile in formato REAL, a nessuno importa se leggo i 32 bit che la compongono uno alla volta, in due variabili da 16 bit o in una sola variabile da 32 bit.

L'unica cosa importante è che devo leggere questi 32 bit da un dispositivo e riportarli pari pari in un altro.

Il fatto che si vadano a leggere questi 32 bit spezzandoli in due variabili da 16 bit (e tra INT e WORD non c'è nessuna differenza), serve solo per far sapere al driver di comunicazione che dovrà leggere 16 + 16 bit. Ma il driver di comunicazione è solo un vettore, e non altera la merce che trasporta. Non effettuerà quindi nessuna conversione di formato.

Una volta ricomposta la variabile (rispettando l'ordine dei byte), si avrà la stessa identica sequenza di 32 bit che c'era in partenza.

Quindi, se il dato di partenza era in formato REAL, non importa a nessuno se ho mentito al driver dicendogli di leggere due word. A destinazione il dato sarà ancora in formato REAL.

Se a un corriere affido due cassette di mele e gli dico che sono pere, lui le consegnerà al destinatario come pere. Ma non basta questo per farle diventare veramente pere. Se sono partite delle mele, arriveranno delle mele.

Modificato: da batta
Link al commento
Condividi su altri siti

Se devo leggere una variabile in formato REAL, a nessuno importa se leggo i 32 bit che la compongono uno alla volta, in due variabili da 16 bit o in una sola variabile da 32 bit.

L'impoortante è che vengano restituiti nel giusto ordine, in altri termini che l'utilizzatore si ritrovi mantissa e caratteristica nell'ordine esatto. Poi come vengano trasferiti non ha nessuna importanza.

Quello che è importante è che il formato real di partenza sia compatibile con quello di utilizzo.

Link al commento
Condividi su altri siti

Io ho realizzato la funzione già da tempo, se vuoi cimentarti con vba non ci sono problemi, ti consiglio di fare una Function in un modulo, in ingresso metti il DINT che hai appena letto, te lo converti in stringa così non ti devi fare paturni con segni o non segni e tratti i bit cosi come sono, estrapoli mantissa segno ed esponente in formato bin... fai 2 for e li ottieni in decimale rimetti gli offset e tutto funziona, ed il giochino è fatto....

Link al commento
Condividi su altri siti

Dovresti convertire tali registri da tipo INT in WORD, ad esempio:

val_basso:=INT_TO_WORD(buffer_ric[0])

val_alto:=INT_TO_WORD(buffer_ric[1])

E in seguito unire i 2 registri di tipo WORD in un registro REAL, ad esempio:

val_flottante:=WORD_AS_REAL(val_basso, val_alto).

Penso abbia completamente ragione Nibble

Nel tuo Caso buffer_ric[0] corrisponde a Lettura[1] e buffer_ric[1] corrisponde a Lettura[2]

Link al commento
Condividi su altri siti

A volte le cose banali sono così banali che qualcuno deve per forza trovare il modo di farle diventare complicate.

Link al commento
Condividi su altri siti

Scusate ma olaffo non è stato preciso,sarebbe bello capire come e perché,comunque se il plc e il M340 e la comunicazione e il MODBUS non ha la possibilità di leggere i registri in formati Real ma solo i singoli indirizzi per cui la word 400 e la word 401 oppure dalla 400 alla 460 in modo consecutivo. Ovvio che poi deve convertire i dati da DINT o da Int... leggendo 2 indirizzi consecutivi e poi convertire tutto in real...

Ora se olaffo ritiene conveniente leggere i 2 interi e poi accodarli per formare il Dint va bene avrà dei motivi,per fare questo basta che fa così...concentratevi e semplice:

RawBinData = Strings.Right("00000000000000000000000000000000" & Convert.ToString(InPutData), 32)

la funzione Convert.ToString converte in stringa ed in numero binario a questo punto gli sia attaccano a sinistro un certo numero di Zeri ed allo stesso tempo si prendono i primi 32 caratteri partendo da destra in modo di ricreare un numero binario a 32 bit completo...

Ora calcoliamo il segno,prendiamo il primo Bit che è quello che esprime il segno..

If Strings.Left(RawBinData, 1) = 1 Then

s = -1

End If

If Strings.Left(RawBinData, 1) = 0 Then

s = 1

End If

la funzione left prendera il primo bit partendo da Sinistra Cioè il segno.

Ora tocca estrapolare l'esponente,possiamo fare così:

Dim Exp As String = ""

Dim ExpResult As Single = 0

ExpResult = 0

Dim I As Integer = 0

Exp = Strings.Left(RawBinData, 9)

Exp = Strings.Right(Exp, 8)

For I = 0 To 7

ExpResult = ((Val(Exp(I))) * 2 ^ (7 - I)) + ExpResult

Next

ExpTot = ExpResult - 127

con il primo left prevevo i primi 9 Bit partendo da sinistra, e con il successivo right prendo igli 8 bit partendo da destra che rappresentano l'esponente.

il ciclo for successivo e semplicemente la conversione da binario a decimale,finita la conversione aggiungo l'offset 127 che rappresenta la propieta dei numeri real nel formato IEEE bla bla bla...

veniamo alla mantissa,che non è altro che i 23 bit a destra mancanti per cui:

Mantissa = Strings.Right(RawBinData, 23)

li prendo come mamma li ha fatti...

For I = 0 To 22

MantissaResult = ((Val(Mantissa(I))) * 2 ^ (22 - I)) + MantissaResult

Next

Dimensione = MantissaResult

MantissaTot = (MantissaResult / 8388608) + 1.0

e li converto in deciamle con il classico for e li divido per l'offset 8388608 ma sto giro gli sommo 1,0 perché il risultato sara sempre 0.....

a questo punto il mio real sarà..

Real = (s * MantissaTot * (2 ^ ExpTot))

Ok penso proprio di essere stato esaustivo e completo nelle informazioni,poi se il problema deve essere ataccare 2 word o 2 interi si puo fare cosi Dint= Convert.ToString(dato1 & dato2)e poi lo si puo dare in pasto alla mia funzione...

Link al commento
Condividi su altri siti

Ma tu stai facendo l'interpretazione di una variabile a 32 bit in formato IEEE-754, non la conversione.

Prendi semplicemente un dato che è già in formato REAL, e visualizzi il valore in numeri decimali e virgola.

Ovvero, significa che i 32 bit sono stati riportati pari pari dal dispositivo A al dispositivo B e, successivamente, interpretati.

Ma se leggi i 32 bit, fai una conversione da DINT a REAL e poi interpreti il valore con la tua funzione (della quale non metto assolutamente in dubbio la correttezza), ottieni un valore che non è quello di partenza.

Facciamo un esempio con i numeri.

Partiamo da una variabile in formato REAL che contiene il valore 1234.5.

Se visualizzo questa variabile in formato esadecimale, vedo 449A5000

Se la visualizzo i binario vedo 0100_0100_1001_1010_0101_0000_0000_0000
Se la visualizzo in decimale vedo 1150963712
Ma sono solo modi diversi di visualizzare la stessa identica variabile.

Se io trasferisco questi 32 bit spezzandoli in due variabili da 16 bit e poi ricompongo la variabile da 32 bit, devo avere ancora 0100_0100_1001_1010_0101_0000_0000_0000

Questi 32 bit se li interpreto come numero in virgola mobile corrispondono a 1234.5.

Se invece decido di convertirli da DINT (o DWORD) in REAL, solo perché il driver di comunicazione mi dice che tratta solo valori interi, la sequenza di 32 bit 0100_0100_1001_1010_0101_0000_0000_0000 (449A5000 Hex)

mi viene convertita in 0100_1110_1000_1001_0011_0100_1010_0000 (4E8934A0 Hex)

Ma se io interpreto 0100_1110_1000_1001_0011_0100_1010_0000 come variabile in formato REAL ottengo 1.150964e+009








Link al commento
Condividi su altri siti

Partiamo da una variabile in formato REAL che contiene il valore 1234.5.

Se visualizzo questa variabile in formato esadecimale, vedo 449A5000

Se la visualizzo i binario vedo 0100_0100_1001_1010_0101_0000_0000_0000

Se la visualizzo in decimale vedo 1150963712

Ma sono solo modi diversi di visualizzare la stessa identica variabile.

Se io trasferisco questi 32 bit spezzandoli in due variabili da 16 bit e poi ricompongo la variabile da 32 bit, devo avere ancora 0100_0100_1001_1010_0101_0000_0000_0000

Questi 32 bit se li interpreto come numero in virgola mobile corrispondono a 1234.5.

Credo che questa risposta di Batta dovrebbe metter fine a tutti i dubbi .....

Aggiungo per completezza che lo scambio di variabili (dati) tramite Modbus (RTU oppure TCP/IP) avviene utilizzando dei registri (WORD) e quindi la difficoltà di Olaffo probabilmente risiede nel fatto che una variabile REAL non può essere mossa 'direttamente' in una variabile DINT SENZA effettuarne una converasione di tipo (con relativa perdita della parte decimale) con una istruzione del tipo REAL_TO_DINT(Floating_var) .....

Per superare questo problema apparente ti basta allocare 1 variabile Real e 1 variabile DInt e farle puntare alla stessa area di memoria (i.e. %MW100) : con questo semplice accorgimento hai risolto il tuo problema !

Link al commento
Condividi su altri siti

Ragazzi Olaffo non e chiaro, pero leggete il software che ha scritto, non sono 2 move sono una conversione IEEE 754 da DINT a real infatti "Conversione Ieee754 -> Real"

valore := ((-1.0)**segno) * (2.0**(Esponente-127.0)) * 1_mantissa;

nel suo software sta cercando di ricavarsi exp mantissa e segno non mi sembra che abbia bisogno di sapere come fare 2 move....

Pero penso che lo abbiamo aiutato abbastanza tante che non si vede...

Link al commento
Condividi su altri siti

Ragazzi Olaffo non e chiaro, pero leggete il software che ha scritto, non sono 2 move sono una conversione IEEE 754 da DINT a real infatti "Conversione Ieee754 -> Real"

Batman, il codice di Olaffo è del tutto superfluo.

Se non può utilizzare le funzioni di cast NATIVE del M340 (DINT_TO_REAL e REAL_TO_DINT) DEVE utilizzare il suggerimento del mio post precedente (ovvero assegnare alla stessa area di memoria i.e. %MW100 una variabile Real e una variabile DINT).

Link al commento
Condividi su altri siti

  • 3 weeks later...
Stefano Sormanni

Ho realizzato un blocco che fa proprio questa funzione , se c'è ancora interesse posso bubblicarlo.

ciao

Link al commento
Condividi su altri siti

  • 11 months later...
  • Giuseppe Signorella locked this discussione
Ospite
Questa discussione è chiusa alle risposte.
×
×
  • Crea nuovo/a...