Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: AlexLazarus - Gennaio 08, 2023, 10:24:54 am

Titolo: [RISOLTO] Da intero a virgola mobile a stringa
Inserito da: AlexLazarus - Gennaio 08, 2023, 10:24:54 am
Sicuramente mi sto perdendo in un bicchier d'acqua, anche se non capisco dove sbaglio.
Dunque...

1) "Come voi ben sapete" (cit.) 75 diviso 2 =  37,5

2) Assegno alla variabile INTERA MioNumero il valore 75:

Var MioNumero : Integer;
...
MioNumero:= 75;
MioNumero:= MioNumero / 2;


Ovviamente(!) Lazarus s'incacchia; e si incacchia pure se modifico Integer con LongInt.
Accetta - altrettanto ovviamente - solo se dichiaro come Real. E fin qui, tutto OK.

3) Ma se io voglio partire da una variabile intera (p.e. una delle proprietà di tipo Integer per un qualsiasi oggetto) come posso effettuare una divisione che fornisce un risultato con decimali e (soprattutto) convertirlo in una stringa da visualizzare in un ShowMessage?


Titolo: Re:Da intero a virgola mobile a stringa
Inserito da: bonmario - Gennaio 08, 2023, 11:05:48 am
Ciao,
non sono sicuro di aver capito la domanda, in ogni caso, "FloatToStr" converte un numero con decimali in stringa. Se vuoi, col secondo parametro gli dici come formattare il numero.

Ciao, Mario
Titolo: Re:Da intero a virgola mobile a stringa
Inserito da: giacomarko - Gennaio 08, 2023, 01:32:34 pm
in alternativa puoi usare la funzione Round (che esegue un arrotondamento del valore float) e restituisce un int64,

questo, se vuoi arrotondare il numero, rispettando il valore decimale,  quindi 75.89 diventa 76

viceversa..

se vuoi solo la parte intera, fregandotene se il valore era 75,89 oppure 75,02

puoi usare Trunc, che ritorna un Int64 del solo valore 75

m
m
Titolo: Re:Da intero a virgola mobile a stringa
Inserito da: DragoRosso - Gennaio 08, 2023, 02:00:42 pm
La divisione con risultato intero deve essere effettuata tramite la funzione DIV:

Codice: [Seleziona]
var
a, b, c: cardinal;

a := b div c;

La problematica di gestione dei numeri è costante ed è stata già affronta più volte nel forum.

Uno dei trucchi è quello di avere i numeri "moltiplicati per il fattore di precisione":

Codice: [Seleziona]
var a_cen, b, c: cardinal;

a_cen := (b*100) div c; //il risultato è in "centesimi" ....

Questa tecnica viene usata normalmente quando si ha a che fare con i sistemi di automazione industriale.

Di altro c'è la fantasia oltre che la normalità già spiegata dai precedenti post.

N.B.: ATTENZIONE ..... 2.0 / 2.0  potrebbe non fare sempre 1  :o

P.S.: non usare definizioni come real, float, o simili. Usa o "single" per la precisione singola o "double" per la precisione doppia che è anche quella più usata per la numerazione in virgola mobile.
Titolo: Re:Da intero a virgola mobile a stringa
Inserito da: AlexLazarus - Gennaio 09, 2023, 08:58:35 am
Tre risposte molto utili: non ricordavo FloatToString(!!)e avevo dimenticato il trucchetto di moltiplicare per 100 / 1000 per poi dividere nuovamente per 100 / 1000. Unica differenza: assegnare la variabile come Real (Cardinal genera errore).
Nello screenshot il mix dei consigli che hanno risolto il problema:
Codice: [Seleziona]
 procedure TForm1.Button1Click(Sender: TObject);
Var Sinistra: Integer;
Var Sin_decimale: Real; // NO Cardinal;

begin
Sinistra:= Button1.Left; // Valore Intero (proprietà .Left)
Sin_decimale:= ((Sinistra*1000) div 3) / 1000; // Variabile Real
ShowMessage('Risultato finale: ' + FloatToStr(Sin_decimale));
end;
                                                     
Titolo: Re:[RISOLTO] Da intero a virgola mobile a stringa
Inserito da: AlexLazarus - Gennaio 09, 2023, 09:13:29 am
A proposito di Real, Single, Double, una cosa non mi torna. Non dovrebbe essere il contrario? (doppia precisione visualizza meno cifre decimali di singola precisione)?
Titolo: Re:[RISOLTO] Da intero a virgola mobile a stringa
Inserito da: nomorelogic - Gennaio 09, 2023, 09:28:59 am
il single viene mappato in memoria con 4 bytes mentre il double con 8
quindi, semplificando, il double ha il doppio delle cifre significative a disposizione rispetto al single

guarda questo specchietto, chiarisce molti dubbi
https://www.freepascal.org/docs-html/ref/refsu5.html
Titolo: Re:[RISOLTO] Da intero a virgola mobile a stringa
Inserito da: AlexLazarus - Gennaio 09, 2023, 09:44:38 am
il single viene mappato in memoria con 4 bytes mentre il double con 8
quindi, semplificando, il double ha il doppio delle cifre significative a disposizione rispetto al single

Guardando lo screenshot precedente sembrerebbe esattamente il contrario. Oppure ho capito male?
Titolo: Re:[RISOLTO] Da intero a virgola mobile a stringa
Inserito da: nomorelogic - Gennaio 09, 2023, 10:14:06 am
potrebbe dipendere dalla formattazione che hai usato per convertire in stringa
(floattostr sceglie il formato più conveniente in modo automatico)

con questo codice, il risultato lo vedi nell'allegato
forzando le cifre decimali a 16, il double è più preciso

Codice: [Seleziona]
procedure TForm1.Button1Click(Sender: TObject);
var s: single;
    d: double;
    sapp: string;
begin
  s := 92 / 3;
  d := 92 / 3;

  sApp := Format('Risultati:' + LineEnding +
                 'single = %20.16f' + LineEnding +
                 'double = %20.16f', [s, d]);

  ShowMessage(sapp);

end;         


Edit:
un altro controllo che puoi fare è quello di guardare il contenuto delle variabili a runtime con il debugger
ma ricorda che si tratta sempre di una conversione in stringa :)


Titolo: Re:[RISOLTO] Da intero a virgola mobile a stringa
Inserito da: DragoRosso - Gennaio 09, 2023, 03:56:08 pm
Da quello che ha postato @nomorelogic capisci che (2 / 2) potrebbe non fare 1.

Il single è visualizzato con le ultime cifre casuali, però vedi che anche nel double nelle due cifre finali qualcosa non torna... questo perchè le cifre significative in double sono circa 16 (e in formattazione ce ne sono 18 "%20.16f").

Ciao
Titolo: Re:[RISOLTO] Da intero a virgola mobile a stringa
Inserito da: DragoRosso - Gennaio 09, 2023, 05:36:11 pm
Create una nuova applicazione, metteteci due pulsanti e fate doppio click sui due pulsanti, poi sovrascrivete tutto col codice qui sotto (basta anche solo la parte implementation):

Codice: [Seleziona]
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }
type
  TConverterRec = packed record
    case Boolean of
      false: (ByteArray: array[0..7] of Byte);
      true: (FloatValue: Double);
    end;

var
  Pippo: TConverterRec;

procedure TForm1.Button1Click(Sender: TObject);
var d: double;
begin
  d := 0.43; // $85EB51B81E85DB3F  Valore 0,43 in double float
  //Pippo.FloatValue := d;
  Pippo.ByteArray[0] := $85;
  Pippo.ByteArray[1] := $EB;
  Pippo.ByteArray[2] := $51;
  Pippo.ByteArray[3] := $B8;
  Pippo.ByteArray[4] := $1E;
  Pippo.ByteArray[5] := $85;
  Pippo.ByteArray[6] := $DB;
  Pippo.ByteArray[7] := $3F;
  ShowMessage('Valore 0,43 = '+floattostr(Pippo.FloatValue));
  //Sommiamo 9 al primo byte byte (per verificare le cifre significative e la rappresentatività)
  inc(Pippo.ByteArray[0], 9);
  ShowMessage('Incremento ultima cifra di 9 del Valore 0,43 = '+floattostr(Pippo.FloatValue));
  //Non cambia .... dobbiamo sommare almeno 10 (9 di prima più 1) al primo byte byte (per verificare le cifre significative e la rappresentatività)
  inc(Pippo.ByteArray[0], 1);
  ShowMessage('Incremento ultima cifra di 10 del Valore 0,43 = '+floattostr(Pippo.FloatValue));
  //però ora facciamo i cattivi: proviamo a sommare il valore precedente (quello che anche se aveva 9 in più veniva visto come 0,43) a se stesso
  dec(Pippo.ByteArray[0], 1);
  ShowMessage('(Incremento ultima cifra di 9 del Valore 0,43) * 2 = '+floattostr(Pippo.FloatValue+Pippo.FloatValue));
  //OHH inciminciano i primi "disturbi ... ma il numero originale avrà anche lui questi problemi ?
  Pippo.FloatValue := d;
  ShowMessage('Valore 0,43 * 2 = '+floattostr(Pippo.FloatValue+Pippo.FloatValue));
  //No, non li ha
end;

procedure TForm1.Button2Click(Sender: TObject);
var d: double;
begin
  d := 100.43; // $EC51B81E851B5940  Valore 100,43 in double float
  //Pippo.FloatValue := d;
  Pippo.ByteArray[0] := $EC;
  Pippo.ByteArray[1] := $51;
  Pippo.ByteArray[2] := $B8;
  Pippo.ByteArray[3] := $1E;
  Pippo.ByteArray[4] := $85;
  Pippo.ByteArray[5] := $1B;
  Pippo.ByteArray[6] := $59;
  Pippo.ByteArray[7] := $40;
  ShowMessage('Valore 100,43 = '+floattostr(Pippo.FloatValue));
  //Sommiamo 9 al primo byte byte (per verificare le cifre significative e la rappresentatività)
  inc(Pippo.ByteArray[0], 9);
  ShowMessage('Incremento ultima cifra di 9 del Valore 100,43 = '+floattostr(Pippo.FloatValue));
  //Non cambia .... dobbiamo sommare 10 (1 di prima più 9) al primo byte byte (per verificare le cifre significative e la rappresentatività)
  inc(Pippo.ByteArray[0], 1);
  ShowMessage('Incremento di 10 nell''ultima cifra del Valore 100,43 = '+floattostr(Pippo.FloatValue));
  //Non cambia ancora.... dobbiamo sommare ulteriormente (non solo al primo byte ma dobbiamo combinare sia il primo che il secondo)

  //Quindi, riassumendo è cambiata la rappresentività del numero, con una piccola variazione (10) ora il valore non sembra cambiare, con il 0,43 si ......
end;

end.

Eseguite e leggete i commenti, vedrete come si comportano apparentemente i numeri ..... o meglio come cambia di significato la rappresentazione: pensate pure se svolgete la semplice addizione mostrata 1000 o 100000 volte nel corso del programma ....

Ciao
Titolo: Re:[RISOLTO] Da intero a virgola mobile a stringa
Inserito da: AlexLazarus - Gennaio 10, 2023, 06:51:24 am
Incredibile, che dire? A quanto pare la verità matematica è ben nascosta nelle pieghe delle impostazioni di base.

Invece io ero rimasto a 2 + 2 = 4.  😐