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

Salve a tutti.
Ho sostituito la funzione originale con la Round2. Sembra funzionare.
Cosa ne pensate?
Compilare con CL XXXX.Prg
Inizio Prova
***************************************************************************
#translate ROUND( <nValue>, <nDecimals> ) => Round2( <nValue>, <nDecimals> )
W_Intero   := 0
W_Decimali := 0
clear screen
while .t.
   IF LASTKEY()=27
      RETURN
   ENDIF
   @  5,  0  SAY "Importo        :" get W_Intero picture "@E 999.999999999999"
   @  7,  0  SAY "Decimali       :" get W_Decimali
   read
   @ 12,  0  SAY replicate(" ",80)
   @ 12,  0  SAY "Importo  Arr.  :"
   @ 12, 17  SAY Round(W_Intero,W_Decimali)
end
****************************************************************************
Fine prova
Function Round2(NumeroCompleto,Decimali)
Local nPosizione      := 0
Local cDecimali       := ""
Local cInteri         := ""
Local cNumeroIn       := ""
Local cNumeroAr       := ""
Local nDecimali       := Decimali +1
Local nArrotandamento := 0
Local nIntero         := 0
Local aScala          := {;
                                     1,;
                                    10,;
                                   100,;
                                  1000,;
                                 10000,;
                                100000,;
                               1000000,;
                              10000000,;
                             100000000,;
                            1000000000,;
                           10000000000;
                          }
Local nDivisore       := 1
Local nSetDec    := Set(_SET_DECIMALS)
Local lSetFix    := Set(_SET_FIXED)
Local nSetDecNew := Decimali
Local lSetFixNew := .T.
Local nDecMax    := Len(aScala)
if Valtype(NumeroCompleto) != "N"
   NumeroCompleto := 0
endif
if Valtype(Decimali) != "N"
   Decimali := 0
endif
nPosizione := At(".",Str(NumeroCompleto))
if nDecimali > nDecMax
   nDecimali := nDecMax
endif
nDivisore       := aScala[nDecimali]
if nPosizione > 0
   Set(_SET_DECIMALS,nSetDecNew)
   Set(_SET_FIXED,lSetFixNew)
   nIntero   := Val(Left( Str(NumeroCompleto), nPosizione -1 ))
   cDecimali := SubStr( Str(NumeroCompleto), nPosizione +1, nDecimali)
   cNumeroIn := Substr(cDecimali,1,nDecimali-1)
   cNumeroAr := SubStr(cDecimali,nDecimali,1)
   if Val(cNumeroAr) >= 5
      nArrotandamento := 1
   endif
   Set(_SET_FIXED,lSetFix)
   Return (nIntero + Val(cNumeroIn)/nDivisore  +  nArrotandamento/nDivisore) 
else
   Set(_SET_DECIMALS,nSetDec)
   Set(_SET_FIXED,lSetFix)
   Return NumeroCompleto
endif
******************************************************************************
-- 
Posted from 
ppp-62-10-138-108.dialup.tiscali.it
 [62.10.138.108] 
via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 Cosa ne pensate?
Io penso che prima dovresti dirmi se trovi qualche problema con la
seguente semplicissima funzione:
#translate ROUND( <nValue>, <nDecimals> ) => Round2( <nValue>,
<nDecimals> )
#define EPS 0.00000000000001
#define ROUND( nValue, nDecimals ) Round( nValue + EPS, nDecimals )
e poi, se ne trovi, fammelo sapere e vediamo il da farsi.
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 tua idea č buona, perň, se non sono io che sbaglio, non funziona con i
numeri negativi.
Andrebbe cosě modificata:
#define EPS 0.00000000000001
#define ROUND(nValue,nDecimals) ;
        IF(nValue>0,Round(nValue+EPS,nDecimals),Round(nValue-EPS,nDecimals))
#define TRANSFORM( xValue, cPicture ) ;
        Transform( If( ValType( xValue )="N",;
                       If(xValue>0 , xValue+EPS , xValue-EPS),;
                       xValue ), ;
                   cPicture )
#define STR( nValue, nDigits, nDecimals ) ;
        Str( IF(nValue>0,nValue+EPS,nValue-EPS), nDigits, nDecimals )
? TRANSFORM( 8.235, "99.99" )  //    => 8.23 ???
? STR( 8.235, 5, 2 )           //    => 8.23 ???
? ROUND( 1.265, 2 )            //    => 1.26 ???
? TRANSFORM("pippo","@!")
? TRANSFORM( -8.235, "99.99" ) //    => 8.23 ???
? STR( -8.235, 5, 2 )          //    => 8.23 ???
? ROUND( -1.265, 2 )           //    => 1.26 ???
Che ne pensi ?
Ciao
Davide.

 numeri negativi.
Giustissimo. Giusta anche la tua soluzione. Propongo anche la mia, forse
appena appena più leggera in quanto evita le due chiamate alla ROUND()
che, pur essendo alternative in esecuzione, sono comunque entrambe
presenti nell'eseguibile:
#define ROUND( nValue, nDecimals ) Round( nValue + If( nValue >= 0, EPS,
-EPS ), nDecimals )
Comunque sarebbe preferibile utilizzare una funzione e la direttiva
#translate in modo da non dover per forza chiamare la funzione ROUND()
tutta in maiuscolo:
- in un nostro .CH mettere
#translate ROUND( <nValue>, <nDecimals> ) => Round2( <nValue>,
<nDecimals> )
- in un PRG di libreria mettere
#define EPS 0.00000000000001
FUNCTION ROUND2( nValue, nDecimals )
    RETURN ROUND( nValue + IF( nValue >= 0, EPS, -EPS ), nDecimals )


 tutta in maiuscolo:
Non avevo compreso appieno l'utilità di #translate.
Ancora una volta, i tuoi spunti sono preziosi. Grazie.
Ciao.
Davide.

 Ancora una volta, i tuoi spunti sono preziosi. Grazie.
Ne sono lieto.


 
L'esecuzione di questa funzione produce un errore di:
UNRECOVERABLE ERROR 650 : Processor Stack Fault.
penso sia meglio la soluzione precedente sostituendo #define con #translate.
-- 
Posted from 
ppp-62-10-142-184.dialup.tiscali.it
 [62.10.142.184] 


 penso sia meglio la soluzione precedente sostituendo #define con #translate.
Con la limitazione, come ho già fatto notare, di poter utilizzare
ROUND() solamente scritta in maiuscolo (e di non essere avvisato in
alcun modo che scrivendo, ad esempio, Round() stai utilizzando quella
vecchia).


Scusami Enrico, confido nella tua perspicacia, continua a dare errore
per questo codice:
round(1.11499999999999,2) ==>> 1.12 invece di 1.11
Help Mario
-- 
Posted from 
ppp-62-10-140-224.dialup.tiscali.it
 [62.10.140.224] 


 round(1.11499999999999,2) ==>> 1.12 invece di 1.11
Hai perfettamente ragione. Prova con questo:
#define EPS 0.000000000000008
FUNCTION ROUND2( nValue, nDecimals )
    RETURN ROUND( nValue + IF( nValue >= 0, EPS, -EPS ), nDecimals )
Vediamo un po' se riusciamo a risolvere.
Fammi sapere.


 round(1.11499999999999,2) ==>> 1.12 invece di 1.11
ho provato a vedere se esiste un altro modo (oltre all'EPS) per correggere
la nostra funzione.
Nella funzione qui sotto si vede innanzitutto che per il tuo valore la round
di clipper è ok, mentre non lo è la mia round2; poi si vede anche che il
valore viene arrotondato automaticamente da clipper durante le diverse
operazioni.
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.
A questo punto mi chiedo:
1) Come hai trovato quel valore che da errore con la EPS ? , è stato un caso
fortuito o è il risultato di qualche operazione nei tuoi gestionali ?
2) Non è che la soluzione perfetta che stiamo cercando in realtà non esiste
?. Mi piacerebbe sapere cosa ne pensa anche Enrico, che ne sa più di me in
quanto a rappresentazione binaria dei numeri.
Ciao.
Davide.
***************
? round2( 1.265, 2 )            //    => 1.26 ???
? round2( 1.11499999999999, 2 )
? round( 1.11499999999999, 2 )
RETURN Nil
Function round2( nValue, nDec)
Local NewValue
NewValue:=nValue*(10^(nDec+1))
? NewValue
NewValue:=int(NewValue+5)
? NewValue
NewValue:=NewValue*(10^-(nDec+1))
? NewValue
Return newValue
***************

 quanto a rappresentazione binaria dei numeri.
Grazie della considerazione ma non ne so poi tanto. Comunque quello che
penso è che il problema è già di per se così poco importante (dal mio
test, che nessuno ha ancora commentato e che se volete riporto
nuovamente, risulta essere presente l'errore dal 5 al 7 per cento circa
dei casi) che dobbiamo trovare una soluzione semplice e via (quella con
l'EPS mi sembra adeguata).


 dei casi)
Non ho visto il test. Puoi ripostarlo per piacere ?
Ciao.
Davide.

Davide, Enrico,
ho cercato di dare una soluzione numerica alla round().
Non ho raggiunto risultati soddisfacenti.
Pero' ragionando con le stringhe mi sembra di aver trovato una soluzione 
ragionevole fino alla 
63.ma
 cifra decimale
con questo codice clipper :
****************************************
//per la verifica
#define EPS 0.00000000000001
#define ROUND(nValue,nDecimals)
IF(nValue>0,Round(nValue+EPS,nDecimals),Round(nValue-EPS,nDecimals))
W_Intero   := 0
W_Decimali := 0
clear screen
while .t.
   IF LASTKEY()=27
      RETURN
   ENDIF
   @  5,  0  SAY "Importo        :" get W_Intero picture "@E
9999999999.99999999999999"
   @  7,  0  SAY "Decimali       :" get W_Decimali
   read
   //Verifica(W_Intero,W_Decimali)
 
   @ 12,  0  SAY replicate(" ",80)
   @ 12,  0  SAY "Importo  Arr.  :"
   @ 12, 17  SAY ROUND(W_Intero,W_Decimali)
   @ 13,  0  SAY replicate(" ",80)
   @ 13,  0  SAY "Importo  Arr.  :"
   @ 13, 17  SAY Round3(W_Intero,W_Decimali)
 
end
*************************************************************
Function Round3(NumeroCompleto,Decimali)
Local nPosizione      := 0
Local cDecimali       := ""
Local cNumeroIn       := ""
Local cNumeroAr       := ""
Local nDecimali       := Decimali +1
Local nArrotandamento := 0
Local nIntero         := 0
Local nDivisore       := 1
Local nSetDecNew      := Abs(int(Decimali))
Local nValIntAssoluto := 0
Local nSetDec         := Set(_SET_DECIMALS)
Local lSetFix         := Set(_SET_FIXED)
Local nMaxDecimali    := 63
if Valtype(NumeroCompleto) != "N"
   NumeroCompleto := 0
endif
if Valtype(Decimali) != "N"
   Decimali := 0
else
   if Decimali >= nMaxDecimali
      nDecimali := nMaxDecimali
   endif
endif
nPosizione := At(".",Str(NumeroCompleto))
nSegno    := If(NumeroCompleto < 0, -1, 1)
if nDecimali <= 0
   Set(_SET_DECIMALS,0)
   Set(_SET_FIXED,.t.)
   nIntero         := Abs(NumeroCompleto)
   nDecimali        := Decimali + (Len(Alltrim(str(nIntero))))
   nDivisore       := Val("1"+Replicate("0", len(Alltrim(Str(nIntero)))))
   NumeroCompleto  := nIntero/nDivisore
   if Abs(decimali) >= len(Alltrim(Str(nIntero)))
      return 0
   endif
   Set(_SET_DECIMALS,nDecimali)
   Set(_SET_FIXED,.t.)
   NumeroCompleto:= NumeroCompleto*nSegno
 
   nArrotondato := Int(Round3(NumeroCompleto,nDecimali)*nDivisore)
   Set(_SET_DECIMALS,nSetDec)
   Set(_SET_FIXED,lSetFix)
   RETURN nArrotondato
endif
nDivisore       := val("1"+Replicate("0",Decimali))
if nPosizione > 0 .and. nDecimali > 0
   Set(_SET_DECIMALS,nSetDecNew)
   Set(_SET_FIXED,.t.)
   nIntero   := Val(Left( Str(NumeroCompleto), nPosizione -1 ))
   cDecimali := SubStr( Str(NumeroCompleto), nPosizione +1)
 
   if Decimali > len(cDecimali)-1
      cDecimali := cDecimali + replicate("0", (Decimali - len(cDecimali)+1))
   endif
 
   cNumeroIn := Substr(cDecimali,1,nDecimali-1)
   cNumeroAr := SubStr(cDecimali,nDecimali,1)
   if Val(cNumeroAr) >= 5
      nArrotandamento := 1
   endif
   nValIntAssoluto := Abs(nIntero)
   nArrotondato :=  (;
            nValIntAssoluto +;
            Val(cNumeroIn+"."+Replicate("0",Decimali))/nDivisore  +;
            nArrotandamento/nDivisore)*nSegno
   Set(_SET_DECIMALS,nSetDec)
   Set(_SET_FIXED,lSetFix)
   Return nArrotondato
else
   Return NumeroCompleto
endif
*************************************************************
 
Mario
-- 
Posted from 
ppp-62-10-136-102.dialup.tiscali.it
 [62.10.136.102] 


 Non ho visto il test. Puoi ripostarlo per piacere ?
Eccolo:
FUNCTION MAIN()
    TEST( 1 )    // ROUND()
    TEST( 2 )    // STR()
    TEST( 3 )    // TRANSFORM()
    RETURN NIL
STATIC FUNCTION TEST( nTipo )
    LOCAL nErr := 0
    LOCAL nTot := 0
    LOCAL i
    FOR i = 0.005 TO 99.995 STEP 0.01
        i = VAL( STR( i ) )
        DO CASE
            CASE nTipo = 1 .AND. ROUND( i, 2 ) = INT( i * 100 ) / 100
                nErr++
            CASE nTipo = 2 .AND. VAL( STR( i, 5, 2 ) ) = INT( i * 100 )
/ 100
                nErr++
            CASE nTipo = 3 .AND. VAL( TRANSFORM( i, "99.99" ) ) = INT( i
* 100 ) / 100
                nErr++
        ENDCASE
        nTot++
    NEXT
    DO CASE
        CASE nTipo = 1
            ? "ROUND()    ", nErr, nTot, LTRIM( STR( nErr / nTot * 100 )
) + "%"
        CASE nTipo = 2
            ? "STR()      ", nErr, nTot, LTRIM( STR( nErr / nTot * 100 )
) + "%"
        CASE nTipo = 3
            ? "TRANSFORM()", nErr, nTot, LTRIM( STR( nErr / nTot * 100 )
) + "%"
    ENDCASE
    RETURN NIL


 ragionevole fino alla 
63.ma
 cifra decimale
Mi devi scusare ma io ritengo che tutto quel codice non sia accettabile
per una cosa semplice come la ROUND(). Ho fatto girare il mio solito
test e vedo un sensibile rallentamento rispetto alla ROUND() del Clipper
e a quella con l'EPS.


Se qualcuno ce l'ha ed è disposto a fare una prova me lo dica.
Sto facendo qualche esperimento in Assembler...per ora senza problemi,
anche se mi riservo di testarlo piu' a fondo.
Utilizzo direttamente la FPU e per me questo e' un campo nuovo.
Ho scartato la soluzione C per due motivi:
- utilizzando la libreria FP e' necessario poi avere la suddetta
libreria per eseguire il link
- utilizzando un altro sistema rende il tutto piu' lento.
Quando avro' qualcosa di preciso mi faro vivo...
ciao, acc.   
a...@libero.it

2) Non è che la soluzione perfetta che stiamo cercando in realtà non esiste?.
A puro titolo documentario, dato che ogni soluzione proposta andrebbe
testata adeguatamente per essere considerata tale, a me risulta, per
le poche prove che ho fatto (sempre su numeri positivi), che il
problema della Round() si manifesta con numeri la cui parte intera è
limitata alle unità.
Di conseguenza, si potrebbe agire non sulla parte decimale, ma su
quella intera sommando una costante (e poi detraendola) in questo
modo:
FUNCTION ROUND2( nValue, nDecimals )
RETURN ROUND( nValue + 100, nDecimals ) -100
Ho verificato poco, ma sembra funzionare.
E' un intervento senza pretese, anche perchè il problema sembra
verificarsi in un numero abbastanza ridotto di casi, per cui è molto
probabile che lasciando le cose come stanno non si corra poi alcun
rischio.
Comunque, la discussione è senz'altro opportuna.
ciao

con questo codice clipper :
Sono d'accordo con il tuo tentativo (che non ho provato).
Non giungendo ad una soluzione soddisfacente tramite calcoli numerici,
io mi rivolgerei appunto alle stringhe.
ciao

 - utilizzando un altro sistema rende il tutto piu' lento.
Non ti seguo. Quale altro sistema? Si tratterebbe semplicemente di
riscrivere la gią contenuta ROUND(), quindi non si dovrebbe aggiungere
niente di nuovo al sistema ma solo correggere un errore.


 RETURN ROUND( nValue + 100, nDecimals ) -100
Niente da fare:
? ROUND2( 1.11499999999999, 2 ) ==> 1.12 ???



Sempre che si possa dimostrare che tale soluzione sia migliore della
mia, dato che č comunque molto lenta.


mia, dato che è comunque molto lenta.
Non hai capito.
IO mi rivolgerei appunto alle stringhe.
Non dovendo partecipare a test di velocità, nè avendo da gestire
programmi infarciti di Round(), me ne frego della velocità e mi affido
alla certezza che la gestione dell'arrotondamento tramite stringhe mi
assicura.
Poi ognuno è libero di fare quello che meglio crede
ciao

? ROUND2( 1.11499999999999, 2 ) ==> 1.12 ???
La risposta di EMG č corretta e sintetica.
Ho voluto verificarla e mi sono reso conto che ha trovato l'errore
utilizzando un numero con una sequenza di undici 9  dopo il 4.
Mentre utilizzando (dopo il 4) una sequenza fino a dieci 9, si ottiene
correttamente 1.11
Non so se esistano altri casi di errore, ma questo segnalato da EMG č
veramente frutto di una situazione limite in cui praticamente (non
siamo in sede scientifica) si starebbe arrotondando il numero 1.115 
con un risultato corretto di 1.12
Credo che, comunque, queste soluzioni basate su calcoli siano tutte
imprecise, non potendosi avvantaggiare della conoscenza del codice
della funzione Round() del Clipper.
ciao


Si, comunque non l'ho capito il tuo punto di vista. In base a quale
criterio scegli una soluzione? Dici di voler scegliere quella basata
sulle stringhe ma non dici esattamente per quale motivo. Se hai trovato
dei problemi con quella numerica allora perché non li esponi così
possiamo esaminarli insieme?


 della funzione Round() del Clipper.
Può darsi che sia come dici ma come possiamo esserne sicuri se non
sottoponiamo tali soluzioni a dei seri test sul campo? Anche le
soluzioni basate sulle stringhe possono nascondere delle insidie...


sottoponiamo tali soluzioni a dei seri test sul campo?
la soluzione basata sulle stringhe non ha bisogno di test.
La mia scelta è fatta.
ciao

soluzioni basate sulle stringhe possono nascondere delle insidie...
quali insidie? non ne vedo.
ciao

 quali insidie? non ne vedo.
Se si vedessero a prima vista non sarebbero insidie. :-)



Oh, ma che avete tutti quanti? Come fai a dire che non ha bisogno di
test? Comunque, contento tu...


niente di nuovo al sistema ma solo correggere un errore.
Mi riferivo a quanto ho gia' scritto sopra. Sono comunque poco
propenso a riscrivere la round(), perche' come del resto tu hai gia'
evidenziato questa non e' l'unica funzione afflitta dal problema.
La mia intenzione e' di scrivere una funzione che prepari il numero
prima di essere dato in pasto alla round() oppure alla str() o alla
transform().
Una cosa del tipo: round(aggiusta(n),6,2)
Per ora sto provando utilizzano la FPU, questo potrebbe causare
conflitti con l'emulatore (integrato in Clipper) anche se finora
problemi in questo senso non ne ho avuti.
Comunque non ho scartato la soluzione di intervenire direttamente su
esponente e mantissa, solo che data la scarsa conoscenza che ho di
questi argomenti (mi sto comunque documentando) ho preferito tenerla
come seconda soluzione.
ciao, acc.   
a...@libero.it

 rischio.
Non sono molto daccordo su questo. I valori sopra esposti potrebbero
tranquillamente essere il risultato dell'applicazione di un aliquota IVA ad
un imponibile in Euro. In questo caso verrebbe stampata in fattura (e
conteggiata) un'imposta errata.
Ciao,
Davide.

 STATIC FUNCTION TEST( nTipo )
purtroppo ho brutte notizie.
Con il test impostato fino a 99.995, la round corretta con l'EPS funziona
bene, ma se alzi il limite a 999.995 vedrai che l'errore si ripresenta con
numeri molto "probabili" tipo 128,015 ; 128,045 ; 128,075 ; 128,105 ....
Tutti questi danno errore sia con la round normale, sia con quella corretta,
con entrambi gli EPS.
Ora faccio qualche prova per affrontare il problema usando le stringhe,
cercando di scrivere poco codice (e quindi pochi errori potenziali) , poi ti
faccio sapere.
Ciao.
Davide.
P.S.: Scusa se ho inoltrato questo messaggio alla tua casella privata.
Potenza di Outlook....

 rischio.
Non sono molto daccordo su questo. I valori sopra esposti potrebbero
tranquillamente essere il risultato dell'applicazione di un aliquota IVA ad
un imponibile in Euro. In questo caso verrebbe stampata in fattura (e
conteggiata) un'imposta errata.
Ciao,
Davide.
P.S.: E' la seconda volta che invio questo messaggio. Il primo è "sparito".
Se è un problema solo del mio client me ne scuso. Ciao.

 faccio sapere.
Si, ho fatto qualche prova anch'io ma la soluzione con le stringhe non
mi soddisfa. La tengo come ultima spiaggia.
Ciao.
EMG
PS. Mando questo messaggio anche al newsgroup in quanto qualcuno
potrebbe essere ancora interessato al discorso.


 come seconda soluzione.
Ok, tienici aggiornati.


 Se è un problema solo del mio client me ne scuso.
Penso di si dato che nel newsgroup sono arrivate entrambe le copie.


 di decimali richiesto. Fammi sapere.
molto bene.
Con il tuo test impostato fino a 9999.995 funziona sempre bene.
Ora gli faccio provare un po di combinazioni in più; al limite lo faccio
anche girare tutta la notte, poi vediamo.
Ciao.
Davide.

conosco pochissimo l'ASM Intel) e OBJ per provarlo?
Ok
ciao, acc.   
a...@libero.it

> molto bene.
Come non detto. Non avevo visto l'altro tuo post, poi ho capito che questa
non poteva funzionare bene.
Proverò l'altra funzione.
Ciao.
Davide.

 Magari c'e' anche qualche guru ex-Nantucket ;-)
Ci pensi tu a contattarli? :-)
