Newsgroup: it.comp.www.php
Titolo: Controllo EURO

Come faccio a fare un controllo usando le espressioni regolari che è in
grado ci riconoscere una valuta EURO?
per esempio  1521.54
Dove il primo numero (prima del punto), può essere da 0 a 9999 e il secondo
numero (dopo il punto) può essere da 00 a 99....,e naturalmente deve esserci
il punto...


fammi vedere se ho imparato bene le espressioni
[0-9]+\.[0-9]+[0-9]+
in questo modo accetterebbe tutto, da 0.00 (attenzione ci devono essere due
cifre dopo la virgola) fino a XXXXXX.00
ho scritto giusto?

 il punto...
Mi sa che questo diventa FAQ...
Fatto: 
http://php.dopoil.com/faq.php?euroRegExp

 ho scritto giusto?
[0-9]+ significa: uno o piu'
quindi potrebbe anche essere 0.0000
[0-9]{2} sarebbe esattamente 2
[0-9]{1,2} se accetti anche una cifra sola quindi 0-99 e non 00-99
quindi:
    [0-9]+\.[0-9]{2}
C'e' da fare molta attenzione per questi Euro, ci sono paesi che lo scrivono
con la virgola e non con il punto. Ed infine abbiamo anche la divisione
10^3. A volte si trovano questi mix:
  3.166,55 Euro ($ 3,330.04)
Allora, chi tira fuori la regexp che accetti tutti modi?
;-)

 Fatto: 
http://php.dopoil.com/faq.php?euroRegExp
Almeno sono stato utile...
E sono felice di aver aperto una nuova sezione nella FAQ...
CMQ, nella faq c'è una piccola correzione da fare (nella prima parte)....
Da come esatto il valore  tipo 0040.60, e non dovrebbe farlo!!!
Per il resto funziona benissimo!!!
Grazie....

 wrote:

Un leading zero non e' difficile da trattare, ci sara' qualcosa come
[1-9][0-9]* all'inizio.
Ma nel nostro caso ci vogliano assertions per farlo bene... (0.50 e' valido
con un zero leading!!).
Quando qualcuno ha fatto la regexp perfetta la mettiamo...

 Quando qualcuno ha fatto la regexp perfetta la mettiamo...
   La perfezione non e` di questo mondo mentre la seguente RE dovrebbe 
riconoscere inserzioni di tipo "italiano" (posto che l'ho scritta con 
mio figlio sulle ginocchia):
	/^(0|(([1-9]\d{0,2})|([1-9]\.\d{3})|([1-9]\d*))),\d{2}$/
e supporta le seguenti "feature":
   1) virgola per separare i centesimi;
   2) uno zero singolo prima della virgola per valori inferiori ad
      un Euro;
   3) separatore delle migliaia (punto) disponibile per i meno pigri
      ma non obbligatorio;
   4) due cifre obbligatorie, invece, per i centesimi.
   L'onore del collaudo al gruppo. ;)
-- 
Alla prossima,
Marco

     /^(0|(([1-9]\d{0,2})|([1-9]\.\d{3})|([1-9]\d*))),\d{2}$/
   Per i fortunati possessori di miliardi (o piu` =) ) di vecchie lire:
	/^(0|(([1-9]\d{0,2})|([1-9](\.\d{3})+)|([1-9]\d*))),\d{2}$/
-- 
Alla prossima,
Marco

 /^(0|(([1-9]\d{0,2})|([1-9](\.\d{3})+)|([1-9]\d*))),\d{2}$/
Se si conferma il funzionamenteo lo metto nella faq (sostituendo ^ e $ con
[^0-9] per un preg_match_all), per caso hai scritto un regression test?

 Allora, chi tira fuori la regexp che accetti tutti modi?
La chicca delle FAQ funziona meglio cosi':
<?php
$expr="/([\d]+[,\.]\d{2})/";
?> 
Almeno prende sia la virgola che il punto.
A volte e' utile avere utenti imbranati...
Comunque la soluzione migliore e' quella di separare euro e centesimi e
poi riassemblarli insieme.
In MySql poi c'e' anche il vantaggio che non bisogna usare campi double, e
per i centesimi basta uno smallint, anche signed.
ciao
-- 
Marco Bernardini - bernama chiocciola libero it
E' bello volare alti e liberi come un'aquila,
ma nessuna talpa e' mai finita nei motori di un jet.

 per i centesimi basta uno smallint, anche signed.
Per fare i conti in Euro senza coinvolgere i numeri frazionari, non conviene
usare solo numeri interi nel programma e nel DB e lavorare in "centesimi"?
Es.: "1234,56 Euro" diventa "123456" centesimi. A livello di interfaccia
si mette poi la virgola.
Ciao,
 ___ 
/_|_\  Umberto Salsi
\/_\/  
www.icosaedro.it

 si mette poi la virgola.
Ehm... non per fare il saccente, ma per prevenirti da possibili e 
dolorissimi errori: è una ca$$ata colossale!!!!
Non si può sostituire l'aritmetica a virgola mobile con quella intera... 
guarda questo semplice esempio:
2,25 * 3,15 = 7,0875 pari secondo le usuali regole a:
7,09 (valore corretto).
Secondo il tuo suggerimento:
2,25 = 225 centesimi
3,15 = 315 centesimi
225*315 = 70875 centesimi
converto all'indietro e ottengo 708,75!!!!
A limite puoi _memorizzare_ i valori nel DB come interi, ma certo non 
farci i conti nell'applicazione... e comunque non vedo l'utilità di 
simili "risparmi" a meno che tu non stia lavorando su un PDA ;-)
bye!
-- 
Fabrizio "Hermooz" Ermini

 [^0-9] per un preg_match_all), per caso hai scritto un regression test?
Sarebbe? Il "regression test", intendo. :)
--------------------------------
Inviato via 
http://usenet.libero.it

 simili "risparmi" a meno che tu non stia lavorando su un PDA ;-)
Il risparmio consiste nel non doversi preoccupare di lavorare in virgola
mobile e quindi nel prevenire i soliti problemi di arrotondamento
che inevitabilmente si possono avere (il numero 0.1 non ha una
rappresentazione finita se espresso in base due, ad esempio, per cui la
rappresentazione in memoria di questo numero deve per forza troncare dei
bit). PHP rappresenta i numeri float in forma binaria, e quindi risente
di questo problema nelle conversioni decimale <-> binario.
Voi come fate?


 Voi come fate?
Ci vorrebbe un ripassino al testo di calcolo numerico per rispondere...
In breve, comunque: QUALUNQUE tipo di rappresentazione finita di un 
numero introduce un errore di arrotondamento nei calcoli, in generale. 
Il che è tuttavia inevitabile perchè al di là della teoria gli unici 
"strumenti" con cui sappiamo fare i calcoli sono i numeri razionali a
precisione finita.
Non è quindi un problema "risolvibile". Quello che si deve fare è 
tenerne conto impiegando per i calcoli una precisione sempre superiore a 
quella effettivamente necessaria. Per problemi scientifici che 
richiedono accuratezza totale c'e' il modo di determinare "quanta" deve 
essere questa precisione; per problemi spiccioli basta, di solito, 
andare di buon senso (i.e. dato che in un campo "valuta" sono 
significative 2 o al massimo 4 cifre decimale, se fai i conti con un 
float o addirittura con un double precision sei abbastanza tranquillo 
direi ;-)). Le usuali regole di rounding, in genere "incorporate" in una 
qualche forma nella maggior parte dei linguaggi sono poi sufficienti a 
gestire la conversione da rappresentazione "precisa" a rappresentazione 
"troncata".


 fate?
 
Nel caso in cui mi occorrano precisioni variabili io preferisco
aggiungere un terzo campo, relativo alla precisione.
Quindi:
$intero1 = 1234;
$decimali1 = 56;
$precisione1 = 2;
$intero2 = 345;
$decimali2 = 789;
$precisione2 = 3;
Numero($intero, $decimali, $precisione) {
return ($intero + ($decimali / (10^$precisione));
}
$num1 = Numero($intero1, $decimali1, $precisione1);
E taglio la testa al toro, chiedendo scusa agli amici di 
www.toroclub.it
Ah, questo trucchetto lo usavo gia' sull'Apple II...


 Sarebbe? Il "regression test", intendo. :)
Un test per controllare modifiche in modo automatico ad un sistema.
Se tu hai un insieme di valori che generano un output ben definito li metti
in una lista e scrivi un programmino che controlli che vengono fuori giusto.
Quando fai una modifica ricontrolli con lo script di non aver introdotto
errori dove prima funzionava. Con regular expressions si possono fare bei
casini.
Forse e' adesso anche chiaro perche' si chiami regression test.
Nel caso dei euro si potrebbe fare un sistemino che riporti il valore in
euro cosi:
(un array di stringhe, valori numerici e booleani ed un piccolo loop che
controlla il risutlato della regexp ed il suo valore ottenuto, solo un
esempio, certo che non e' codice php ;-)).
'0.50', 50, true
'x.50', 0, false
'1.50', 150, true
'1,50', 150, true
'123.23,50', 0, false
'123.345,50', 12334550, true
'123,345,50', 0, false
'.345,50', 0, false
'00,50', 0, false
// ecco un caso che non abbiamo nella regexp
// e che potrebbe servire! ;-)
'-1.50' -> -150 => true
ecc.

 [^0-9] per un preg_match_all), per caso hai scritto un regression test?
   No di certo: non immaginavo che ci potessero essere evoluzioni. ;)
-- 
Alla prossima,
Marco

 wrote:

Hm, qui ho visto gia' varie versioni. L'ultima cosa che mi riccordo
sono valute negative e fare un test piu' rigoroso su modalita'
1,234.50 o 1.234,50. Accettare magari anche il modo molto tedesco: 2,-
(per dire 2,00) o semplice 2 ecc. Con un po' di fantasia viene fuori
qualcosa di complesso...
;-)

 ;-)
   Ulp! Qui non serve un'espressione regolare, serve un parser in piena 
regola! =)
   Qualche considerazione personale, smentibilissime:
   1) Le valute negative non possono essere accettabili: per quel
      poco che so di contabilita`, la "negativita`" di una quantita`
      di denaro e` valutata dalla classe contabile alla quale
      e` associata (dare o avere, attivita` o passivita`) ed il
      computo algebrico e` demandato ai programmi che gestiscono
      la contabilita` stessa, per cui non mi preoccuperei di inserire
      un controllo sul segno nella RegEx;
   2) La scelta tra il formato #.###,## ed il formato #,###.##
      puo` essere agevolmente demandata alla localizzazione del
      sistema, per cui si potrebbero scrivere N RegEx, una per
      ogni localizzazione, oppure tentare una parametrizzazione,
      sempre in funzione della localizzazione, ma non inserire
      tutto nella stessa RegEx;
   3) Allo stesso modo, si puo` operare con la "anglosassonizzazione"
      dei decimali, la quale, in Italia, credo che manchi solo a
      causa del lunghissimo tempo dal quale non si vedevano i
      decimali stessi;
   4) I calcoli in Euro, per normativa europea, devono essere
      effettuati con cinque cifre decimali, ed il risultato
      arrotondato alla seconda cifra decimale, per cui suggerirei,
      come formato di memorizzazione in un'eventuale base di dati,
      il dominio DECIMAL(10,2) [1] oppure DECIMAL(10,5) [2];
   5) Per il calcolo l'uso dell'algebra in virgola fissa con tanti
      bit per i decimali quanti ce ne vogliono per rappresentarne
      cinque (sedici, quindi) o, poiche` non mi sembra che il
      PHP supporti interi a sessantaquattro bit (che, tolti i sedici
      per i decimali, rimangono abbastanza lunghi anche per gestire
      i milioni), addirittura la scrittura di un'apposita classe
      che surroghi il tipo Currency di Microsoft ed integri la
      nostra RegEx in fase di costruzione [3];
   Insomma: io avevo proposto una soluzioncina per un problemino, se il 
problemino diventa problemone... ;)
[1] Avendo cura di non lasciar fare calcoli al DBMS.
[2] Se non si puo` fare a meno di usare le funzioni aggregate.
[3] La quale, se ho tempo, posso sempre scriverla io.
-- 
Alla prossima,
Marco

 si mette poi la virgola.
Ripesco un thread di qualche settimana fa perche' casca a fagiolo col
problema che ho riscontrato oggi. Tutti noi abbiamo ormai imparato a fare
i conti in Euro e centesimi, e ormai sappiamo bene che 57 centesimi meno
56 centesimi fanno 1 centesimo, giusto? Sbagliato! Provare per credere:
$ php -q
<? echo(0.57-0.56); ?>
0.0099999999999999
In realta' nulla di strano: lo sappiamo che la differenza calcolata,
e cioe' 0.1, non ha una rappresentazione finita se espressa in base 2,
come invece avviene dentro la memoria del PHP. La risposta che ci da'
il PHP e' assolutamente corretta entro la precisione di calcolo prevista
dai numeri in virgola mobile.
Se il risultato precedente facesse parte di un calcolo appena piu'
complesso, ecco che i risultati presto diverrebbero inattesi e otterremo
di emettere fatture per 10.0099999999999999 Euro, quadrature di bilancio
errate e altri fenomeni paranormali.
Si potrebbe essere tentati di eseguire un arrotondamento al centesimo
dei risultati ottenuti, evitando cosi' di mandare al commercialista una
fattura da 10.0099999999999999 Euro:
$ php -q
<? echo( round(0.57-0.56,2)); ?>
0.01
Questo fornisce il risultato corretto, ma rendiamoci conto che
stiamo ricorrendo a un trucco cosmetico per reinserire una parte
dell'informazione che abbiamo perduto: la conversione delle stringhe
"0.56" e "0.57" nella forma binara ha sottratto irrimediabilmente
informazione, e la funzione round() non fa altro che sottrarre altra
informazione. Siamo ancora lontani dal poter garantire che la quadratura
di bilancio, una volta fatti tutti i calcoli, non ci dia una robe
del tipo:
ATTIVITA' = 10000.00 Euro
PASSIVITA' = 9999.99 Euro
Panico!
Il vero problema sta nel PHP, che non ha un supporto per un tipo numerico
adatto ai calcoli monetari. Per quello che ne so', l'unico modo per
aggirare il problema e' quello di eseguire i calcoli esprimendo gli
importi in centesimi, e lavorando quindi solo con numeri interi: bisogna
fare molta attenzione a quello che si scrive, ma almeno non si deve piu'
lottare coi capricciosi numeri in precisione reale.


 
credo sia un post molto utile, magari da inserire nelle FAQ
ciao
Alesi_27

 lottare coi capricciosi numeri in precisione reale.
Giusta osservazione, ma i 32 bit vengono facilmente sfiorati da valori euro
oltre i 21.474.836,48(che non sono neanche tanti, pero' sul mio conto
corrente non ci sono questi problemi ;-). E questo causa dei problemi in php
perche' si comincia ad usare float senza sapere niente. Per programmi in
ambiente bancario, finanziaro non la soluzione migliore.
Alla fine si puo' usare la libreria bc per risolvere questi problemi.
Settando un fattore di scale (
it.php.net/bcscale
) tipo 5 (mi sembra il
valore per fare conversioni euro standard, ma non sono sicuro).

 lottare coi capricciosi numeri in precisione reale.
   Il PHP possiede una libreria in grado di operare su numeri reali 
espressi come stringhe con precisione e scala grandi a piacere. Con un 
minimo di decadimento delle prestazioni, si esce dal panico. ;)
 
http://www.php.net/manual/en/ref.bc.php
-- 
Alla prossima,
Marco

 valore per fare conversioni euro standard, ma non sono sicuro).
La libreria bcmath potrebbe essere utile per risolvere la questione
dei calcoli in Euro. Certo, e' un po' una palla dover usare le varie
funzioni di calcolo per fare semplici somme e sottrazioni e schivare
eventuali conversioni implicite fatte a tradimento dall'interprete tra
int e bc.
Grazie a te a Marco per la segnalazione molto utile!
Avete gia' fatto esperienza con questa libreria applicata ai calcoli in Euro?
E' affidabile?


 int e bc.
Hm, pensando bene mi sa che vanno anche bene i float (64bit IEEE = double in
'C') per certi tipi di calcolo.
Importante pero' di usare un fattore di scaling quando si salva, inserisce
in form, si fa aritmetica come moltiplicazione, divisione e ovviamente le
potenze (utile per tassi). E usare in modo rigoroso il round.
nel esempio di Umberto [0.57-0.56] l'errore esiste gia' a livello del 0.56
(rappresentato in double come .5600000000000001)
Con un fattore di scaling come 10000 (sempre per il fatto che i calcoli per
le conversioni si fanno con un certo numero di cifre di arrotondamento) si
ottiene:
5700-5600=100
quando si rappresenta il numero abbiamo 100/10000 --> 0.01
per una moltiplicazione tra due cifre non si corrono rischi:
  5600*a (PHP) = 0.56 * a *10000 (mondo ideale);
  IVA: 5600*20/100 -> 0.56*20/100*10000
anche se non ha senso per calcoli finanziari, moltiplicare con se stesso
occorre di applicare il fattore al risultato dividendo con il fattore
stesso:
a/10000*b/10000 = c/(10000*10000); -> a*b/10000 = c/10000;
Non avendo frazioni i calcoli verranno fatti in modo giusto, senza perdere
precisione (divisione ovviamente esclusa, per quello si impone un fattore
superiore alla valuta portando errori a livello basso)
Solo in fase di rappresentazione necessita' poi l'applicazione del fattore:
  echo round($val/10000,2);
A noi ci interessa ora quanto preciso potrebbe essere, e dato che 64 bit
double usa 52 bit (11bit per l'esponente e 1 bit per il segno) arriviamo con
un fattore di 10000 ad un numero massimo equivalente a
4.503.599.627.370.496/10000 450.359.962.737 Euro (spero di arrivarci con il
prossimo bilancio) sfruttando la piena precisione dei double considerando
anche arrotondamenti. Con una precisione di un centesimo si arriva a 45
billioni di Euro.
JM1/20000C ;-)