Newsgroup: it.comp.lang.visual-basic
Titolo: [?] Gestione valori numerici con cifre decimali.

Ciao a tutti,
Configurazione: Win NT 4.0 WKS, SP3, VB6.0 Professional ita, ADO 2.5 (mi
accorgo ora che non ho reinstallato le SP di VB).
Il problema è relativo alla gestione dei valori numerici con cifre decimali
inseriti dall'utente e nasce dalla constatazione che un numero con cifre
decimali a piacere, trasformato in double con l'istruzione CDbl(), ritorna
valori differenti a seconda delle impostazioni internazionali definite sul
PC che esegue il prg (mi riferisco all'impostazione internazionale relativa
al "separatore decimale" nella tab "numeri").
Cioè, io imposto il separatore "," eseguo il prg e tutto va bene (a meno
dell'indice 3 che utilizza la funzione Val()); poi imposto il separatore
".", eseguo il prg ed il numero che avevo inserito risulta senza separatore
decimale (sempre a meno dell'indice 3 che utilizza la funzione Val()).
Ho provato, vedi codice, diverse soluzioni, ma nessuna mi sembra torni
valori corretti. Di seguito il codice che ho scritto; nella form ho messo
una serie di textbox ed un bottone; nella prima inserisco il numero ed al
click sul bottone:
    Dim Doub1 as Double
    Doub1 = CDbl(txtText1(0).Text)
    txtText1(1).Text = CStr(Doub1)
    txtText1(2).Text = Trim(Str(Doub1))
    txtText1(3).Text = Val(txtText1(0).Text)
    txtText1(6).Text = FormatNumber(txtText1(0).Text, 2, vbTrue, vbFalse,
vbTrue)
Quello che chiedo è: è corretto il comportamento che mi si verifica?
Nell'help di VB è scritto che CDbl() converte in double utilizzando le
impostazioni internazionali. Ed allore perchè modificando il carattere
separatore decimali tutto va a pallino ?
Su un precedente portatile avevo installato Win 2000 SP3, VB6 Pro Eng SP5,
ADO 2.5 e l'anomalia si verificava lo stesso (questo potrebbe escludere che
il problema sia legato alla mancanza delle SP di VB6).
A qualcun'altro succede lo stesso ? Che strada ha intrapreso questo
qualcun'altro per risolvere l'anomalia ?
Inoltre nella pagina dell'help titolata "impostazioni internazionali"
raggiungible dalla pagina "funzioni di conversione del tipo" si legge
testualmente
"...
Le impostazioni internazionali del sistema agiscono sulle funzioni che
dipendono dal paese, ad esempio la visualizzazione di numeri o la
conversione di stringhe in date. Le impostazioni internazionali vengono
definite dalle utilità del Pannello di controllo fornite dal sistema
operativo.
Le impostazioni internazionali del codice equivalgono in genere a quelle del
sistema, anche se in alcuni casi possono essere diverse. In Visual Basic
edizione Standard e Professional, ad esempio, il codice non viene tradotto
dall'impostazione Inglese (USA). Anche se le impostazioni internazionali del
sistema vengono definite in base alla lingua e al paese dell'utente, le
impostazioni internazionali del codice saranno sempre Inglese (USA) senza
possibilità di modifica. In questi casi vengono pertanto utilizzati i
separatori, i segnaposti di formato e l'ordinamento di tipo Inglese (USA)."
Mi aiutate a capirne il significato ?
Grazie in anticipo a chi ha avuto la pazienza di leggermi.
Robertuan

 
Qui ti sta dicendo che nel codice non viene tenuto conto delle
impostazioni del pannello di controllo.
Nel codice per indicare tre e mezzo devi scrivere 3.5, sempre,
altrimenti il codice scritto su un pc con una lingua non sarebbe
compilabile su uno in cui è impostata una lingua differente!
-- 
Samuele Battarra
prova il mio newsreader:
http://digilander.iol.it/battarsa/programmi/news/

 compilabile su uno in cui č impostata una lingua differente!
Scusa, ma questo non mi č ancora molto chiaro. Per "codice" intendi
l'ambiente di sviluppo di VB6 ? Quindi, facendo girare il prg da ambiente
IDE si avrebbero risultati differenti rispetto all'esecuzione del medesimo
prg. da ambiente Windows ?
Ciao
Roberto Omedei

Roberto Omedei ha scritto:
Confesso di averci passato un po' di tempo, ma non mi stava bene quello che,
provando, avevo scritto. Mi riprometto di riprovarci perchè la cosa mi
interessa, non che abbia dei dubbi particolari, ma volevo farne "oggetto di
studio" e un piccolo sunto. Per intanto lancio un sasso, probabilmente
inutilmente:
Quando cambi il separatore interi/decimali nelle impostazioni
internazionali, rammenti di cambiare (in senso opposto) anche quello delle
migliaia, vero? In caso contrario, ovvero se i due separatori sono identici
(non ricordo se applicabile nel contesto) accadono cose allucinanti.

 migliaia, vero?
Si, in realtà ho fatto prove modificando entrambi i separatori e prove
modificando solo il separatore decimale ... ma nel mio caso non ho rilevato
differenze di risultato.
Ciao
Roberto Omedei

 Controllo -> Impostazioni Internazionali).
Allora, riprendiamo il messaggio originale.
Hai il seguente codice:
    Dim Doub1 as Double
    Doub1 = CDbl(txtText1(0).Text)
    txtText1(1).Text = CStr(Doub1)
    txtText1(2).Text = Trim(Str(Doub1))
    txtText1(3).Text = Val(txtText1(0).Text)
    txtText1(6).Text = FormatNumber(txtText1(0).Text, 2, vbTrue, vbFalse, vbTrue)
Per rendere più chiare l'esposizione, racchiuderò i numeri tra ""
quando sono delle stringhe e tra '' quando sono dei double, in
quest'ultimo caso userò sempre la virgola come separatore decimale.
Supponiamo che nelle I.I. (impostazioni internazionali)
separatore decimale -> ,
separatore migliaia -> .
e che txtText1(0).Text contenga "12,34"
CDbl restituirà il valore corretto: '12,34'
CStr(Doub1) riconverte il double in stringa restituendo "12,34"
Str(Doub1) converte anc'hessa in stringa ma restituisce "12.34" in
quanto questa funzione non tiene conto delle I.I.
Val(txtText1(0).Text) cerca di convertire la stringa "12,34" in
un numero, ma poichè questa funzione non usa le I.I. quando incontra
il carattere "," si ferma e restituisce '12'
infine la funzione FormatNumber, che segue le I.I. restituisce "12,34"
(piccola nota, la sintassi corretta sarebbe:
FormatNumber(txtText1(0).Text, 2, TristateTrue, TristateFalse, TristateTrue)
Supponiamo invece che nelle I.I.
separatore decimale -> .
separatore migliaia -> ,
e che txtText1(0).Text contenga "12.34"
CDbl restituirà il valore corretto: '12,34'
CStr(Doub1) riconverte il double in stringa restituendo "12.34"
Str(Doub1) converte anc'hessa in stringa e restituisce "12.34"
Val(txtText1(0).Text) converte la stringa "12.34" in '12,34'
infine la funzione FormatNumber, restituisce "12.34"
Ora vediamo questo caso, che è quello che forse può fuorviare
separatore decimale -> .
separatore migliaia -> ,
txtText1(0).Text = "12,34"
CDbl si trova di fronte ad una stringa contenente un separatore
delle migliaia in una posizione in cui non ha senso che ci sia.
Forse la cosa migliore sarebbe che la funzione segnalasse l'errore.
Chi l'ha scritta invece ha preferito ignorare i separatori delle
migliaia presenti nella stringa, senza stare a controllare se questi
siano o no nelle posizioni giuste, per cui la stringa "12,34" viene
convertita nel numero '1234'.
A questo punto CStr e Str si conportano di conseguenza, restituendo
la stringa "1234".
La funzione Val, trovandosi davanti la stessa stringa del primo caso,
restituisce lo stesso valore, '12'.
Infine la funzione FormatNumber, di fronte al numero '1234'
restituisce la stringa "1,234.00"
Ora non sto a descrivere il caso con separatore decimale ",", separatore
migliaia "." e stringa in input "12.34", se hai capito quest'ultimo caso
capirai che il comportamento è analogo.
Chiudo con un'osservazione personale.
Io come separatore decimale uso ".", lo so che da noi si usa la virgola,
ma quando si inseriscono dati col tastierino numerico li c'è il punto,
pochi programmi fanno la conversione automatica e doversi spotare
sull'altra tastiera per digitare la "," lo trovo scomodo.
Di conseguenza come separatore delle migliaia NON uso "," che mi pare
esteticamente orribile, ma un bell'apice "'".


 Controllo -> Impostazioni Internazionali).
Allora, riprendiamo il messaggio originale.
Hai il seguente codice:
    Dim Doub1 as Double
    Doub1 = CDbl(txtText1(0).Text)
    txtText1(1).Text = CStr(Doub1)
    txtText1(2).Text = Trim(Str(Doub1))
    txtText1(3).Text = Val(txtText1(0).Text)
    txtText1(6).Text = FormatNumber(txtText1(0).Text, 2, vbTrue, vbFalse, vbTrue)
Per rendere più chiare l'esposizione, racchiuderò i numeri tra ""
quando sono delle stringhe e tra '' quando sono dei double, in
quest'ultimo caso userò sempre la virgola come separatore decimale.
Supponiamo che nelle I.I. (impostazioni internazionali)
separatore decimale -> ,
separatore migliaia -> .
e che txtText1(0).Text contenga "12,34"
CDbl restituirà il valore corretto: '12,34'
CStr(Doub1) riconverte il double in stringa restituendo "12,34"
Str(Doub1) converte anc'hessa in stringa ma restituisce "12.34" in
quanto questa funzione non tiene conto delle I.I.
Val(txtText1(0).Text) cerca di convertire la stringa "12,34" in
un numero, ma poichè questa funzione non usa le I.I. quando incontra
il carattere "," si ferma e restituisce '12'
infine la funzione FormatNumber, che segue le I.I. restituisce "12,34"
(piccola nota, la sintassi corretta sarebbe:
FormatNumber(txtText1(0).Text, 2, TristateTrue, TristateFalse, TristateTrue)
Supponiamo invece che nelle I.I.
separatore decimale -> .
separatore migliaia -> ,
e che txtText1(0).Text contenga "12.34"
CDbl restituirà il valore corretto: '12,34'
CStr(Doub1) riconverte il double in stringa restituendo "12.34"
Str(Doub1) converte anch'essa in stringa e restituisce "12.34"





-- 



Mantenendo inalterato il valore della tab ImpostazioniInternazionali, a me i
dati non vengono visualizzati come da te descritti ...
io ottengo:
CDbl restituisce: '1234'
CStr(Doub1) restituisce"1234"
Str(Doub1) restituisce: "1234"
Val(txtText1(0).Text) restituisce: "12.34" in '12,34'
FormatNumber restituisce: "12.34"
Da ancora altre prove, nel mio caso mi è sembrato di capire che CDbl()
trasforma correttamente se c'è coerenza tra il valore impostato nella tab
ImpostazioniInternazionali ed i valori dei separatori decimali e migliaia
nella tab "Numeri".
Cioè, in altre parole, tutto funziona correttamente se vengono lasciati i
separatori definiti automaticamente in seguito al valore impostato in
ImpostazioniInternazionali, mentre viceversa ottengo i valori sopra
indicati.
Io utilizzo un ocx che inserisce nel campo il separatore decimale impostato
nelle I.I. nella tab "Numeri" alla pressione dei caratteri normalmente
utilizzati come separatori decimali ("," e ".") e quindi non mi preoccupo di
cosa digita l'utente.
Ciao.
Roberto Omedei

 dati non vengono visualizzati come da te descritti ...
Ma io non ho lasciato inalterato il valore della tab
se guardi ho invertito tra loro , e .
Facciamo una cosa.
Manda tu un esempio indicando tutte le condizioni iniziali come ho
fatto io, poi di seguito i valori che tu ti aspetti e quelli che invece
ricevi.



In Pannello di Controllo --> Impostazioni Internazionali io (Win NT 4 sp 6)
vedo le seguenti tab:
1 ImpostazioniInternazionali
2 Numeri
3 Valuta
4 Ora
5 Data
6 Impostazioni Internazionali di input
Nel mio messaggio mi riferivo alla tab 1, mentre tu hai variato i separatori
che sono nella tab 2.
Facendo alcune prove per arrivare a capire, ho notato che variando il valore
della tab 1 (ad esempio da Italiano(Standard) a Inglese(StatiUniti)),
vengono variati di conseguenza anche i valori della tab 2 (il che mi sembra
assolutamente corretto).
Per tornare ai nostri esempi attesi e ... disattesi:
1) Supponiamo che nelle I.I. (impostazioni internazionali)
    tab 1: Italiano(Standard)
    tab 2: separatore decimale -> , separatore migliaia -> .
    e che txtText1(0).Text contenga "12,34"
    I dati vengono visualizzati esattamente come dici tu (valori coerenti).
2) Supponiamo che nelle I.I. (impostazioni internazionali)
    tab 1: Italiano(Standard)
    tab 2: separatore decimale -> . separatore migliaia -> ,
    e che txtText1(0).Text contenga "12.34"
    Io ottengo i seguenti valori:
    CDbl restituisce: '1234'
    CStr(Doub1) restituisce"1234"
    Str(Doub1) restituisce: "1234"
    Val(txtText1(0).Text) restituisce: "12.34"
    FormatNumber restituisce: "12.34"
3) Supponiamo che nelle I.I. (impostazioni internazionali)
    tab 1: Inglese(StatiUniti)
    tab 2: separatore decimale -> . separatore migliaia -> ,
    e che txtText1(0).Text contenga "12.34"
    Io ottengo i seguenti valori:
    CDbl restituisce: '12.34'
    CStr(Doub1) restituisce"12.34"
    Str(Doub1) restituisce: "12.34"
    Val(txtText1(0).Text) restituisce: "12.34"
    FormatNumber restituisce: "12.34"
E' come se CDbl() trasformasse correttamente una stringa in un double solo
se i valori delle tab 1 e 2 sono coerenti tra loro.
Infatti nei casi 1 e 3 è tutto OK, mentre nel caso 2 no.
Ciao
Roberto Omedei

 
Più che dalla coerenza tra tab 1 e 2, potrebbe dipendere dalla coerenza
tra tab 2 e 3. Infatti ho sentito da più parti che se nelle due tabelle
sono impostati separatori diversi vb da problemi.
Nel caso tu vari la tab 1 il problema non si presenta perchè lui in
automatico ti varia sia la tab 2 che la 3.
Inoltre farei anche un'altra prova.
Prima di cDbl metti queste istruzioni
debug.print "sep. dec. -> "; mid(format(1000,"#,###.##"), 2, 1)
debug.print "sep. mig. -> "; mid(format(1000,"#,###.##"), 6, 1)
per vedere quali separatori vb "percepisce" nei vari casi


 per vedere quali separatori vb "percepisce" nei vari casi
Già fatto e i separatori corrispondono a quelli impostati nella tab 2; non
solo, ho anche utilizzato l'apposita API, ma i separatori risultanti sono
identici a quelli rilevati utilizzando la funzione format.
Alla fine ho dovuto inserire un controllo per bloccare un eventuale valore
nella tab 1 al di fuori di una serie da me predefinita e un secondo
controllo per segnalare la non coerenza tra il valore nella tab1 ed i valori
della tab 2. Non sono riuscito a trovare un'altra soluzione.
Non è proprio il massimo, confido che gli utenti siano solo italiani o
inglesi !!!
Ciao
Roberto Omedei

 
No, anch'io al tempo l'ho cercato senza risultato.
Le api più promettenti sembrerebbero queste, ma non ho approfondito
il discorso più di tanto.
EnumSystemLocales
GetLocaleInfo
GetCurrencyFormat
