Newsgroup: it.comp.lang.vo-clipper
Titolo: Round() e Clipper 5.2e

Con questa funzione mi sembra che il problema della round sia corretto.
Arrotonda solo fino alla decima cifra decimale e la parte intera può
contenere al massimo 14 cifre (13 se il numero è negativo), ma in compenso è
piuttosto semplice e non dovrebbe nascondere casi "imprevisti".
Con queste cifre comunque credo che si possa arrotondare correttamente
qualsiasi importo in Euro.
Si può ancora migliorare, comunque provatela un po, poi fatemi sapere.
Ciao.
Davide.
Function Round2(nNum,nDec)
Local cNum,cInt,cDec,cUlt
cNum:=Str(nNum,25,10)
cInt:=left(cNum,14)              // Parte Intera
cDec:=SubStr(cNum,16,nDec)       // Parte Decimale
cUlt:=SubStr(cNum,16+nDec,1)     // Un decimale in piu' per le verifiche
If cUlt>="5"                      // Se e' >= 5 ..
  cDec:=lTrim(Str(Val(cDec)+1))   // .. aumento di 1 la parte decimale
  Do Case
    Case Len(cDec)>nDec           //    Se e' aumentato troppo (es. 99->100)
..
      cDec:="0"                     //    .. i decimali vanno a zero ..
      cInt:=lTrim(Str(Val(cInt)+1)) //    .. aumento di 1 la parte intera
    Case Len(cDec)<nDec           //    Se e' diminuito (es. 00->1) ..
      cDec:=Right(Replicate("0",10)+cDec,nDec) //  .. fillo di 0 a sx
  EndCase
Endif
Return Val(cInt+"."+cDec)

Si può ancora migliorare, comunque provatela un po, poi fatemi sapere.
E' una buona soluzione, considerata anche l'ora in cui l'hai
scritta... 
Certamente l'obiettivo proncipale è quello di gestire correttamente
l'Euro.
Io ho verificato qualche caso (sempre numeri positivi) e mi sembra
tutto ok.
L'unico problema è quello segnalato recentemente del 1.11499999999999,
per il quale la tua Round2() dà un risultato di 1.12.
Siamo nella situazione in cui il numero da arrotondare ha una sequenza
consistente di 9, per cui viene trattato dal Clipper come 1.115
Anch'io ho realizzato una funzione di arrotondamento che risolve anche
quest'ultimo caso.
Ho preferito lavorare su numeri interi, perchè il buon Clipper, ora è
sempre più chiaro, fa parecchi casini sui numeri decimali (anche con
la INT()... ).
function roundA( nNUM, nDEC )
	LOCAL nAPP, nULT
	// moltiplicando per 10 elevato nDEC + 1
	// ricavo un numero la cui parte intera comprende
	// anche la cifra da valutare per l'arrotondamento
	nAPP := nNUM * 10^(nDEC+1)
 
	// tronco la parte decimale
	// ottenendo un numero intero
	nAPP := INT( nAPP )
 
	// a causa di alcuni problemi con INT()
	// controllo il numero intero ottenuto
	IF ( nAPP/10^(nDEC+1) ) > nNUM
		nAPP--
	ELSEIF ( nAPP/10^(nDEC+1) ) < nNUM
		nAPP++
		IF ( nAPP/10^(nDEC+1) ) > nNUM
			nAPP--
		ENDIF
	ENDIF
 
	// ricavo la cifra da valutare per l'arrotondamento
	nULT := VAL( RIGHT( STR( nAPP ), 1 ) )
 
	// prendo ora il numero da arrotondare
	// sempre in forma intera
	nNUM := INT( nNUM * 10^nDEC )
 
	// lo incremento, se necessario
	nNUM += IF( nULT >= 5, 1, 0 )
 
	// lo riporto in forma decimale
	nNUM /= 10^nDEC
 
RETURN nNUM

 Si può ancora migliorare, comunque provatela un po, poi fatemi sapere.
Scusa, non ho capito se con questo avrebbe dovuto funzionare o meno (non
funziona):
? ROUND2( 1.11499999999999, 2 ) ==> 1.12 ???
Inoltre non funziona neanche questo
? ROUND2( -123.456, -1 ) ==> -123 ???
EMG
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg

 la INT()... ).
Qualche esempio dei problemi con la INT() di cui parli, per poterci
lavorare sopra?

Purtroppo non funziona bene. Qualche esempio:
? ROUNDA( -123.456, -1 ) ==> -120.00 ???
? ROUND( -123.456, -1 ) ==> -120 OK!
Potrebbe dare problemi di allineamento. Inoltre:
? ROUNDA( -1.11499999999999, -1 ) ==> 0.00 ???


Come detto, la INT() dà problemi, per cui la funzione da me proposta
va modificata per effettuare un controllo anche sul secondo utilizzo
della INT().
function roundA( nNUM, nDEC )
	LOCAL nAPP, nULT
	// moltiplicando per 10 elevato nDEC + 1
	// ricavo un numero la cui parte intera comprende
	// anche la cifra da valutare per l'arrotondamento
	nAPP := nNUM * 10^(nDEC+1)
 
	// tronco la parte decimale
	// ottenendo un numero intero
	nAPP := INT( nAPP )
 
	// a causa di alcuni problemi con INT()
	// controllo il numero intero ottenuto
	IF ( nAPP/10^(nDEC+1) ) > nNUM
		nAPP--
	ELSEIF ( nAPP/10^(nDEC+1) ) < nNUM
		nAPP++
		IF ( nAPP/10^(nDEC+1) ) > nNUM
			nAPP--
		ENDIF
	ENDIF
 
	// ricavo la cifra da valutare per l'arrotondamento
	nULT := VAL( RIGHT( STR( nAPP ), 1 ) )
 
	// prendo ora il numero da arrotondare
	// sempre in forma intera
	nAPP := INT( nNUM * 10^nDEC )
	// a causa di alcuni problemi con INT()
	// controllo il numero intero ottenuto
	IF ( nAPP/10^(nDEC+1) ) > nNUM
		nAPP--
	ELSEIF ( nAPP/10^(nDEC+1) ) < nNUM
		nAPP++
		IF ( nAPP/10^(nDEC+1) ) > nNUM
			nAPP--
		ENDIF
	ENDIF
	// lo incremento, se necessario
	nAPP += IF( nULT >= 5, 1, 0 )
 
	// lo riporto in forma decimale
	nNUM := nAPP / 10^nDEC
 
RETURN nNUM

? ROUNDA( -123.456, -1 ) ==> -120.00 ???
lo accennavo qualche riga sopra, le mie prove e la funzione si
limitano ai soli numeri positivi.
Per i casi in cui la INT() si comporta in modo anomalo, basta che
durante i test su numeri decimali controlli il suo operato.
Se ti dico INT( 512.175) tronca correttamente, ma durante le prove su
variabili valorizzate tramite incrementi progressivi, mi ha dato
risultati inesatti.
ciao

 della INT().
Sbaglio qualcosa io oppure quest'ultima versione non funziona per
niente? Esempi:
? ROUNDA( 1.11499999999999, 2 ) ==> 1.12 ???
? ROUNDA( 1.11599999999999, 2 ) ==> 1.13 ???
? ROUNDA( -1.11499999999999, -1 ) ==> -10.00 ???
? ROUNDA( -123.456, -1 ) ==> -130.00 ???
? ROUNDA( 123.5, 0 ) ==> -125.00 ???


Purtroppo, prove ulteriori sulla funzione da me proposta rilevano
molti più errori di quelli rilevabili nella funzione di Davide, che
invece, nei limiti di quanto ha detto e per i numeri positivi, sembra
non sbagliare mai.
ciao.

 risultati inesatti.
Se ho capito quello che intendi, non è un problema della INT() ma della
solita rappresentazione floating-point. Comunque dammi qualche esempio
concreto che me lo studio.



Hai ragione, non avevo pensato al caso in cui il numero di decimali potesse
essere negativo.
Ora non ho tempo, ma piů tardi vedo come si puň correggere.
Ciao.
Davide.

 per il quale la tua Round2() dà un risultato di 1.12.
Vedi la risposta data ad EMG. Possiamo lavorarci su se ne vale la pena.
Ciao.
Davide.


Uno l'ho postato anch'io il 14/11/01, sempre relativamente alla round:
*******************************
Inoltre, per far funzionare la round2 anche per il valore 1.265 ho dovuto
elevare il numero per (nDec+1) invece che per nDec, altrimenti la INT() non
funziona.
*******************************
Sembra che abbia gli stessi problemi della round(). fai un piccolo ciclo,
come nella tua funzione di test e te ne renderai conto.
E' per questo che la mia round2 controlla se il decimale successivo a quello
voluto è >="5" (stringa). Nella prima stesura avevo semplicemente aggiunto
.5 e fatto int(), ma.....
Ciao.
Davide.

solita rappresentazione floating-point. 
Sě, certo. Ma la INT() dovrebbe (dico, dovrebbe) troncare il numero
separandolo dalla parte decimale, ed invece si permette di interferire
con la parte intera.
Purtroppo, ho mollato la INT() e non so riproporti i diversi casi che
mi sono capitati.
Ho modificato ulteriormente la RoundA(), semplificandola, ma, pur
migliorando, rimangono ancora dei problemi.
ciao.

Enrico, 
prova con il tuo EPS dinamico la serie di numeri:
0.00400, 2.00400, 3.00400, ..., 541.00400
sono le prime differenze che ho trovato.
ciao

 Ora non ho tempo, ma più tardi vedo come si può correggere.
Ok.


 come nella tua funzione di test e te ne renderai conto.
Ecco un test che non rileva alcun errore riguardante la INT():
FUNCTION MAIN()
    LOCAL nErr := 0
    LOCAL nTot := 0
    LOCAL i, j
    FOR i = 0 TO 99
        FOR j = 0.001 TO 0.999 STEP 0.001
            j = VAL( STR( j ) )
            IF INT( i + j ) != i
                nErr++
            ENDIF
            nTot++
        NEXT
    NEXT
    ? "INT()", nErr, nTot, LTRIM( STR( nErr / nTot * 100 ) ) + "%"
    RETURN NIL


 mi sono capitati.
Peccato.



Grazie. Sembra invece che l'ultima mia proposta funzioni bene anche con
questi valori.



Ok, la soluzione è piuttosto semplice. Solo una IF in più per ricondurre
questo caso a quello normale.
Se trovi qualche altro caso non previsto, fammelo sapere.
Ciao.
Davide.
Function Round2(nNum,nDec)
Local cNum,cInt,cDec,cUlt,nDec1:=0
If nDec<=0
  nNum:=nNum*10^nDec             // Divido il numero per traslare la virgola
  nDec1:=nDec                    // Ricordo num.decimali
  nDec:=0                        // Numero decimali = 0 per round
Endif
cNum:=Str(nNum,25,10)
cInt:=left(cNum,14)              // Parte Intera
cDec:=SubStr(cNum,16,nDec)       // Parte Decimale
cUlt:=SubStr(cNum,16+nDec,1)     // Un decimale in pi- per le verifiche
If cUlt>="5"                      // Se S >= 5 ..
  cDec:=lTrim(Str(Val(cDec)+1))   // .. aumento di 1 la parte decimale
  Do Case
    Case Len(cDec)>nDec           //    Se S aumentato troppo (es. 99->100)
..
      cDec:="0"                     //    .. i decimali vanno a zero ..
      cInt:=lTrim(Str(Val(cInt)+1)) //    .. aumento di 1 la parte intera
    Case Len(cDec)<nDec           //    Se S diminuito (es. 00->1) ..
      cDec:=Right(Replicate("0",10)+cDec,nDec) //  .. fillo di 0 a sx
  EndCase
Endif
Return Val(cInt+"."+cDec)/10^nDec1


Ho riguardato il mio vecchio post e credo di aver realizzato che il problema
che in prima analisi avevo imputato ad INT() in realtà è invece dovuto alla
riga precedente, dove viene fatto l'elevamento a potenza.
Infatti quella riga non printa esattamente 126.5 , ma 126.50000 (ecco perchè
sia la mia round2 che la roundA di Diego in qualche caso mostrano i decimali
non significativi).
Inoltre, se fai INT(126.5+.5) , ritorna correttamente 127 , scagionando
definitivamente la INT().
Ciao.
Davide.
? round2( 1.265, 2 )    //    => 1.26 ???
? int(126.5+.5)         //    => 127 ok.
RETURN Nil
Function round2( nValue, nDec)
Local NewValue
NewValue:=nValue*(10^(nDec))
? NewValue                      // 126.5  ok.
NewValue:=int(NewValue+.5)
? NewValue                      // 126 ???
NewValue:=NewValue*(10^-(nDec))
? NewValue                      // 1.26
Return newValue

 Se trovi qualche altro caso non previsto, fammelo sapere.
Ok, restano sempre i casi come
? ROUND3( -123.456, -1 ) ==> -120.00
nei quali la presenza dei decimali spuri (non presenti nella ROUND()
originale) può causare problemi di allineamento nelle stampe (vorrei che
non sottovalutaste il problema).


 definitivamente la INT().
Bene.
