Discussione:
funzione round, funziona?
(troppo vecchio per rispondere)
alfmicon
2007-11-17 06:19:00 UTC
Permalink
In un controllo di maschera se ho un valore 0,0050 e la maschera di input
mostra solo due cifre, quello che vedo è 0,01 cioè un valore arrotondato
matematicamente (se il valore dopo la cifra da arrotondare non supera 49
l'arrotondamento è per difetto, se 50 o superiore l'arrotondamento è per
eccesso).
La stessa cosa mi aspetto se utilizzo la formula: round(0.0050, 2)
E invece no, il risultato è 0.
Allora devo necessariamente introdurre un correttivo ad esempio:
round(0,0050 + 0,0001, 2) che produce 0,01
Nel calcolo dell'IVA c'è pittosto da impazzire.
Qualcuno mi potrebbe spiegare se sia logico dover introdurre un correttivo
per ottenere un risultato matematicamente corretto?
Karl Donaubauer
2007-11-17 08:10:32 UTC
Permalink
Post by alfmicon
In un controllo di maschera se ho un valore 0,0050 e la maschera di input
mostra solo due cifre, quello che vedo è 0,01 cioè un valore arrotondato
matematicamente (se il valore dopo la cifra da arrotondare non supera 49
l'arrotondamento è per difetto, se 50 o superiore l'arrotondamento è per
eccesso).
La stessa cosa mi aspetto se utilizzo la formula: round(0.0050, 2)
E invece no, il risultato è 0.
round(0,0050 + 0,0001, 2) che produce 0,01
Nel calcolo dell'IVA c'è pittosto da impazzire.
Qualcuno mi potrebbe spiegare se sia logico dover introdurre un correttivo
per ottenere un risultato matematicamente corretto?
www.donkarl.com/it?FAQ2.1
--
HTH
Karl
*********
Access FAQ: www.donkarl.com/it
Bruno Campanini
2007-11-18 13:45:29 UTC
Permalink
Post by alfmicon
In un controllo di maschera se ho un valore 0,0050 e la maschera di input
mostra solo due cifre, quello che vedo è 0,01 cioè un valore arrotondato
matematicamente (se il valore dopo la cifra da arrotondare non supera 49
l'arrotondamento è per difetto, se 50 o superiore l'arrotondamento è per
eccesso).
La stessa cosa mi aspetto se utilizzo la formula: round(0.0050, 2)
E invece no, il risultato è 0.
round(0,0050 + 0,0001, 2) che produce 0,01
Nel calcolo dell'IVA c'è pittosto da impazzire.
Qualcuno mi potrebbe spiegare se sia logico dover introdurre un correttivo
per ottenere un risultato matematicamente corretto?
Prova questa:
Public Function EuroRound(ByVal ValueToRound, _
Optional DecimalDigit As Byte = 2)
ValueToRound = CDec(ValueToRound)
EuroRound = (Sgn(ValueToRound) * (Fix(Abs(ValueToRound) _
* 10 ^ DecimalDigit + 0.5) / 10 ^ DecimalDigit))
End Function

Bruno
alfmicon
2007-11-19 07:09:00 UTC
Permalink
Mi rifiutavo di pensare che in un linguaggio Microsoft ci fosse un errore
così grossolano di una funzione matematica.
Mi ero già attrezzato ponendo il correttivo. Dai vostri interventi mi sono
convinto che ciò è necessario.
Vi ringrazio per i suggerimenti che mi avete dato ma credo che questa
function sia la soluzione migliore:
Aggiunge un decimale piccolo a seconda del numero dei decimali rilevati dal
valore da arrotondare ed utilizza la stessa funzione round()


Public Function Arrotonda(Valore As Double, Optional Decimali As Integer =
2) As Double
Dim n As Integer ‘numero dei decimali presenti
n = Len(CStr(Valore)) - Len(CStr(Int(Valore))) + 1 ‘ +1 impedisce di
diventare 0 in caso il valore sia intero
Arrotonda = Round(Valore + (1 / 10 ^ n), Decimali)
End Function

E poichè il massimo numero di decimali che viene accettato in access è 15 si
potrebbe semplificare in questo modo:

Arrotonda = Round(Valore + 0,000000000000001), Decimali)

mi sembra che funzioni sempre

grazie a tutti
giorgio rancati
2007-11-19 10:20:16 UTC
Permalink
Post by alfmicon
Mi rifiutavo di pensare che in un linguaggio Microsoft ci fosse un errore
così grossolano di una funzione matematica.
e infatti non c'è nessun errore, la funzione Round di VB-VBA utilizza un
metodo di arrotondamento denominato Banker's rounding ovvero quando un
numero si trova a metà tra due numeri, viene arrotondato alla cifra pari più
vicina.
Poi, se questo metodo non è applicabile per ottenere un arrotondamento
commerciale, questo non vuol dire che la funzione sia bacata.
Post by alfmicon
Mi ero già attrezzato ponendo il correttivo. Dai vostri interventi mi sono
convinto che ciò è necessario.
Vi ringrazio per i suggerimenti che mi avete dato ma credo che questa
Aggiunge un decimale piccolo a seconda del numero dei decimali rilevati dal
valore da arrotondare ed utilizza la stessa funzione round()
Public Function Arrotonda(Valore As Double, Optional Decimali As Integer =
2) As Double
Dim n As Integer 'numero dei decimali presenti
n = Len(CStr(Valore)) - Len(CStr(Int(Valore))) + 1 ' +1 impedisce di
diventare 0 in caso il valore sia intero
Arrotonda = Round(Valore + (1 / 10 ^ n), Decimali)
End Function
E poichè il massimo numero di decimali che viene accettato in access è 15 si
Arrotonda = Round(Valore + 0,000000000000001), Decimali)
mi sembra che funzioni sempre
mica tanto, a parte le implicazioni dell'uso di un tipo di dato
approssimato, con un valore negativo in ingresso la funzione rende un
risultato sbagliato (commercialmente parlado)
es.
----
? Arrotonda(-1.005)
Risultato: -1
----

Ciao
--
Giorgio Rancati
[Office Access MVP]
alfmicon
2007-11-19 21:46:01 UTC
Permalink
Post by giorgio rancati
Post by alfmicon
Mi rifiutavo di pensare che in un linguaggio Microsoft ci fosse un errore
così grossolano di una funzione matematica.
e infatti non c'è nessun errore, la funzione Round di VB-VBA utilizza un
metodo di arrotondamento denominato Banker's rounding ovvero quando un
numero si trova a metà tra due numeri, viene arrotondato alla cifra pari più
vicina.
Poi, se questo metodo non è applicabile per ottenere un arrotondamento
commerciale, questo non vuol dire che la funzione sia bacata.
Post by alfmicon
Mi ero già attrezzato ponendo il correttivo. Dai vostri interventi mi sono
convinto che ciò è necessario.
Vi ringrazio per i suggerimenti che mi avete dato ma credo che questa
Aggiunge un decimale piccolo a seconda del numero dei decimali rilevati dal
valore da arrotondare ed utilizza la stessa funzione round()
Public Function Arrotonda(Valore As Double, Optional Decimali As Integer =
2) As Double
Dim n As Integer 'numero dei decimali presenti
n = Len(CStr(Valore)) - Len(CStr(Int(Valore))) + 1 ' +1 impedisce di
diventare 0 in caso il valore sia intero
Arrotonda = Round(Valore + (1 / 10 ^ n), Decimali)
End Function
E poichè il massimo numero di decimali che viene accettato in access è 15 si
Arrotonda = Round(Valore + 0,000000000000001), Decimali)
mi sembra che funzioni sempre
mica tanto, a parte le implicazioni dell'uso di un tipo di dato
approssimato, con un valore negativo in ingresso la funzione rende un
risultato sbagliato (commercialmente parlado)
es.
----
? Arrotonda(-1.005)
Risultato: -1
----
Giorgio, hai ragione, il range dei numeri negativi mi era del tutto sfuggito.
Crreggerei la riga di programma così:

Round(Valore + IIf(Valore > 0, 0.0000000000001, -0.0000000000001), Decimali)

Inoltre non conoscevo il metodo Banker's rounding. Mi sembrava comunque che
almeno nella visualizzazione dei dati su una maschera e sul calcolo della
funzione di arrotondamento ci dovesse essere una coerenza.
Non per giustificarmi, ammetto l'ignoranza, ma un programmatore è
disorientato se i progettisti dei linguaggi seguono criteri diversi
all'interno di uno stesso ambiente.
In ambiente excel è ancora diverso. E la documentazione non fornisce molto
aiuto.
Grazie di tutto
giorgio rancati
2007-11-19 22:55:22 UTC
Permalink
Post by alfmicon
Giorgio, hai ragione, il range dei numeri negativi mi era del tutto sfuggito.
Round(Valore + IIf(Valore > 0, 0.0000000000001, -0.0000000000001), Decimali)
sì, ma poniamo di voler arrotondare dei numeri memorizzati in un campo di
tipo Singola precisione.
Proviamo a vedere cosa succede:
( nell'esempio sotto simulo il tipo di dato Singola precisione con la
funzione Csgn() )
----
? Arrotonda(Csng(7.995))
Risultato: 7,99
----
come vedi non ci siamo ancora :-)
Post by alfmicon
Inoltre non conoscevo il metodo Banker's rounding. Mi sembrava comunque che
almeno nella visualizzazione dei dati su una maschera e sul calcolo della
funzione di arrotondamento ci dovesse essere una coerenza.
Non per giustificarmi, ammetto l'ignoranza, ma un programmatore è
disorientato se i progettisti dei linguaggi seguono criteri diversi
all'interno di uno stesso ambiente.
In ambiente excel è ancora diverso.
la funzione VBA Round() di Excel funziona esattamente come in VBA di Access,
non potrebbe essere altrimenti visto che l'ambiente è lo stesso.
Mentre la funzione Arrotonda del foglio di Excel esegue l'arrotondamento
commerciale.
Post by alfmicon
E la documentazione non fornisce molto
sì, vero, qui puoi trovare maggiori info
----
How To Implement Custom Rounding Procedures
http://support.microsoft.com/kb/196652/en-us
----

:-)
--
Giorgio Rancati
[Office Access MVP]
alfmicon
2007-11-20 08:45:00 UTC
Permalink
Post by giorgio rancati
Post by alfmicon
Giorgio, hai ragione, il range dei numeri negativi mi era del tutto sfuggito.
Round(Valore + IIf(Valore > 0, 0.0000000000001, -0.0000000000001), Decimali)
sì, ma poniamo di voler arrotondare dei numeri memorizzati in un campo di
tipo Singola precisione.
( nell'esempio sotto simulo il tipo di dato Singola precisione con la
funzione Csgn() )
----
? Arrotonda(Csng(7.995))
Risultato: 7,99
----
come vedi non ci siamo ancora :-)
Post by alfmicon
Inoltre non conoscevo il metodo Banker's rounding. Mi sembrava comunque che
almeno nella visualizzazione dei dati su una maschera e sul calcolo della
funzione di arrotondamento ci dovesse essere una coerenza.
Non per giustificarmi, ammetto l'ignoranza, ma un programmatore è
disorientato se i progettisti dei linguaggi seguono criteri diversi
all'interno di uno stesso ambiente.
In ambiente excel è ancora diverso.
la funzione VBA Round() di Excel funziona esattamente come in VBA di Access,
non potrebbe essere altrimenti visto che l'ambiente è lo stesso.
Mentre la funzione Arrotonda del foglio di Excel esegue l'arrotondamento
commerciale.
Post by alfmicon
E la documentazione non fornisce molto
sì, vero, qui puoi trovare maggiori info
----
How To Implement Custom Rounding Procedures
http://support.microsoft.com/kb/196652/en-us
----
Giorgio,
mi hai fattto scoprire il mondo degli arrotondamenti.
In realtà si hanno valori diversi nel caso:
?Csng(-7.995)
-7,995
e nel caso il dato si invii byVal ad una funzione.
La funzione, che si aspetta un duble, trasforma il dato ricevuto in:
-7,99499988555908
e il perchè di questo proprio non lo capisco (per cui arrotonda(),
giustamente restituisce -7,99)

Lo puoi vedere anche così:
?cdbl(Csng(-7.995))
-7,99499988555908

I valori vengono rispettati se l'origine è Ccur(-7.995) . Perchè??
?ccur(Csng(-7.995))
-7,995

?cdbl(ccur(-7.995))
-7,995

Ovviamente si generano dati che alla fine risultano inaffidabili, o no? o
uno si deve settare un linguaggio personalizzato prima di sviluppare un
programma?
Bruno Campanini
2007-11-20 09:32:46 UTC
Permalink
Post by alfmicon
Giorgio,
mi hai fattto scoprire il mondo degli arrotondamenti.
?Csng(-7.995)
-7,995
e nel caso il dato si invii byVal ad una funzione.
-7,99499988555908
e il perchè di questo proprio non lo capisco (per cui arrotonda(),
giustamente restituisce -7,99)
?cdbl(Csng(-7.995))
-7,99499988555908
I valori vengono rispettati se l'origine è Ccur(-7.995) . Perchè??
?ccur(Csng(-7.995))
-7,995
?cdbl(ccur(-7.995))
-7,995
Ovviamente si generano dati che alla fine risultano inaffidabili, o no? o
uno si deve settare un linguaggio personalizzato prima di sviluppare un
programma?
Non sono gli algoritmi (formule) ad essere inaffidabili,
è il floating point di VBA (Single e Double) a non esserlo.
Quindi se non usi i Type Currency o Decimal il problema
non lo risolvi.
La formula che ti ho inviato lavora in tal senso e non sbaglia.

Allora i Single e i Double li usi quando non ti servono
precisioni millimetriche. Li usi, soprattutto il Double, per
grandezze astronomiche o microscopiche dove quel che conta
è l'ordine di grandezza e non la precisione.
Per i calcoli commerciali dovresti usare Currency che può
consentire fino a quattro decimali e ti dà l'assoluta precisione.

Bruno
giorgio rancati
2007-11-20 09:46:58 UTC
Permalink
Post by alfmicon
Giorgio,
mi hai fattto scoprire il mondo degli arrotondamenti.
?Csng(-7.995)
-7,995
e nel caso il dato si invii byVal ad una funzione.
-7,99499988555908
e il perchè di questo proprio non lo capisco (per cui arrotonda(),
giustamente restituisce -7,99)
?cdbl(Csng(-7.995))
-7,99499988555908
sì, certamente, infatti avevo scritto:
----
Post by alfmicon
a parte le implicazioni dell'uso di un tipo di dato approssimato
----
bene, questi sono gli effetti dell'uso di un tipo di dato che per sua natura
è approssimato, effetti determinati dalla struttura binaria del tipo di dato
e che vanno benissimo se effettivamente stiamo trattando un numero
approssimato (area di un cerchio, logaritmo di un numero, seno di un angolo
ecc) ma quando si parla di importi monetari questi effetti non vanno bene e
bisogna affidarsi a un tipo di dato non approssimato.
Post by alfmicon
I valori vengono rispettati se l'origine è Ccur(-7.995) . Perchè??
?ccur(Csng(-7.995))
-7,995
?cdbl(ccur(-7.995))
-7,995
Ovviamente si generano dati che alla fine risultano inaffidabili, o no? o
uno si deve settare un linguaggio personalizzato prima di sviluppare un
programma?
assolutamente no, basta affidare il calcolo a un tipo di dato non
approssimato, vedi
----
http://web.tiscali.it/giorgiorancati/msaccess/documents/arrotonda.htm
----
o anche la funzione che ha proposto Bruno.

Entrambi gli esempi affidano il calcolo al tipo di dato Decimal che non è
approssimato ed ha la precedenza sugli altri tipi quindi restituisce il
risultato nel tipo Decimal.
Copia la funzione in un modulo Bas e provala nella peggiore delle situazioni
ovvero passando in ingresso un singola precisione.
----
? Arrotonda(Csng(-7.995))
Otterrai il risultato -8

? Arrotonda(Csng(7.995))
Otterrai il risultato 8
----

Ciao
--
Giorgio Rancati
[Office Access MVP]
alfmicon
2007-11-20 23:06:01 UTC
Permalink
Sono arrivato alla conclusione che la soluzione Bruno è effettivamente la
migliore.
Purtroppo ho utilizzato qua e la diversi campi duble dai quali cercherò pian
piano di liberarmi.
Vi ringrazio, Bruno e Giorgio, per il tempo dedicato a questo problema
ciao

Continua a leggere su narkive:
Loading...