Newsgroup: it.comp.lang.c++
Titolo: Numero di cifre decimali

Come si può ottenere il numero di cifre decimali di un numero con la
virgola? Sapete suggerirmi qualche funzione (anche non standard, scusatemi
se sono OT) o algoritmo?
Es.: 3, 5678: 4 cifre decimali - 21,45: 2 cifre decimali.
Possibilmente il numero di riferimento dovrebbe essere un long double o un
double.
Kaos.

 wrote in message
news:NgEea.94182$pG1.2...@news1.tin.it...


 Come si puň ottenere il numero di cifre decimali di un numero con la

La prima cosa sulla quale devi riflettere e' che quasi tutti i numeri
decimali, quando sono memorizzati in un double (o long double), sono
imprecisi.
In particolare
il numero 3.12 in realta' corrispondere al numero
caso (1) 3.12000000000000010658...
mentre 2.17 corrisponde in realta' al numero
caso (2) 2.16999999999999992895
In entrambi i casi sopra tu vorresti (secondo il buon
senso) avere il risultato 2.
Capisci bene quindi che il problema va affrontato pensandoci un
momentino su'.... anche per definire bene
cosa intendiamo per numero di cifre decimali....
Inoltre, se provi a fare un po di stampe con printf("%40.20f\n",d);
di un numero double capisci perche' non puoi evitare
di prendere in considerazione anche la grandezza della
parte intera, anche se ti interessano solo i decimali.
se per esempio invece di 3.12 (come sopra) consideriamo
623423213.12
allora avremo in realta'
623423213.1200000047683....
se confronti le due parti decimali vedi che sono diverse
0.12000000000000010658...
0.1200000047683....
questo ovviamente e' ragionevole, ma bisogna
tenerne conto, altrimenti, se vogliamo che
nel primo caso la risposta sia 2, nel secondo
sia 2, e che in un eventuale caso con
  d= 1.32340000002400004497
la risposta sia (come ci aspetteremmo) 12.
Per ora ti lascio con queste considerazioni.
caso mai lunedi per un po' di codice.
ciao,
g.

 wrote in message
b5f3iq$9je$1...@news.flashnet.it...


 > Come si può ottenere il numero di cifre decimali di un numero con la


 wrote in message
news:O3Gea.133729$zo2.3...@news2.tin.it...


 persuadermi a farlo.
Avere una idea di massima su come sono rappresentati
i numeri in floating-point secondo me e' FONDAMENTALE
se fai cose che riguardano i numeri in floating-point.
Se conosci gia' la rappresentazione in base 2 allora la
cosa e' piuttosto semplice.
C'e' un articolo molto interessante che consiglio a
tutti:
"What Every Computer Scientist Should Know About Floating-Point 
Arithmetic" D.Goldberg (ACM Surveys)
http://cch.loria.fr/documentation/IEEE754/ACM/goldberg.pdf
versione piu' recente (aggiornata ma non so se c'e' tutto
il materiale vecchio):
http://cch.loria.fr/documentation/IEEE754/ACM/goldbergSUN.ps
ciao,
g.

 wrote in message
b5f3iq$9je$1...@news.flashnet.it...


 definito le operazione tra un numero non fratto e un oggetto Frazione. 
Secondo me faresti bene ad evitare di definirla.
E' chiaro che puoi definire una funzione che "piu' o meno" di da il
numero di decimali che "ad occhio" una persona vedrebbe in un
numero. Pero' tieni conto che questo funzionerebbe solo per frazioni
particolari. Gia' se uno scrive d = 2.0/3.0 ottiene un numero d
che ha un numero di decimali massimo (0.6666666...fino a che c'e'
precisione) e quindi poi andresti a moltiplicare la tua frazione
per un numero tipo 66666666667/10000000000 che oltre a essere
comunque "sbagliato" ti farebbe esplodere numeratori e denominatori.
(a meno che tu non stia implementando un pacchetto a precisione
arbitraria, con i numeri interi andresti poco avanti).
In pratica avrebbe senso solo per numeri tipo 6.345
che sono stati inseriti cosi' o sono stati prodotti da una divisione per
1000 e allora tanto vale continuare a fare operazioni intere sulle
frazioni.
Secondo me le 2 soluzioni sensate sono:
1) NON ammettere la moltiplicazione (e altre operazioni) per numeri
floating-point
2) obbligare l'utente a specificare esplicitamente quante cifre decimali
  vuole considerare del numero floating point.
ciao.
g.

 dei numeri con virgola, potrei persuadermi a farlo.
Non sempre vale quello. Il suo valore è "intorno" 3.12, e la precisione
di questo intorno è definita in float.h col valore DBL_EPSILON per i
double e FLT_EPSILON per i float. A dire il vero non sono sicuro ne'
dei nomi, ne se esiste un cfloat.
-- 
Andrea Bergia - studente              Linux Registred User #281550
"The one thing we're all waiting for, is peace on Earth and an end to
war,
it's a miracle we need, the miracle, the miracle, 
Peace on earth and end to war today,
That time will come one day you'll see when we can all be friends"

 > Però, ad un certo punto del lavoro, mi sono reso conto che non avevo


 perchč far fare al pc calcoli precisi č molto difficile.
non e` del tutto vero: semplicemente stai usando gli strumenti sbagliati.
I numeri floating point sono stati progettati per essere efficienti (sia 
in termini di tempo che di spazio), non per essere precisi.
Esistono altre tecniche di rappresentazione dei numeri che sono piu` 
lente o occupano piu` spazio ma che sono molto piu` precise, come ad 
esempio i numeri a virgola fissa e precisione arbitraria: il C++ non li 
supporta direttamente ma probabilmente trovi in giro diverse librerie 
utili in questo senso.
Nota che la tua classe Frazione e` proprio un esempio di rappresentazione 
numerica piu` lenta ma piu` precisa dei floating point: ad esempio con 
le frazioni e` possibile memorizzare *esattamente* il valore di 2/3, 
cosa che non e` possibile fare con i floating point.
ciao Anto
-- 
"Computer science is not about computers any more than astronomy
is about telescopes." -- EW Dijkstra

 numerica piu` lenta ma piu` precisa dei floating point
Sono sulla giusta strada allora! :)
Kaos.

 };
non confondere il *numero* dalla sua *rappresentazione*: il fatto che un 
determinato numero sia periodico dipende solo ed esclusivamente dal modo 
in cui decidiamo di scriverlo: prendi, ad esempio, il valore 1/3: in 
base 10 (quella usuale) e` periodico, mentre in base 3 non lo e`.
Infine, non ho capito perche` hai bisogno di una classe "Periodico": i 
numeri periodici sono un sottoinsieme proprio dei numeri razionali (aka 
"frazioni"), per cui la tua classe "Frazione" copre gia` tutti i numeri 
periodici, in qualsiasi base essi siano espressi.


 Infine, non ho capito perche` hai bisogno di una classe "Periodico"
Stavo divagando, č un'ipotesi. Infatti non la sto realizzando.
Grazie, Kaos.

 risultato. Tecnicamente in C++ il dato astratto viene così "informatizzato":
(scusa, ero in vacanza).
Allora.
Dobbiamo assolutamente distinguere 2 problemi
1) Conversione in frazione di un numero decimale inserito in input
dall'utente.
2) Conversione in frazione di un numero decimale ottenuto attraverso
    operazioni (come ad esempio 0.51121/19.331+0.441)
il primo problema e' banale.
Siccome il numero in input viene fornito come una stringa di
char, e' sufficiente fare un elementare parsing della stringa per
vedere esattamente quante cifre decimali ha nella mente dell'utente
e non nella rappresentazione del computer.
puoi fare qualcosa tipo (traducilo tu in c++  migliore, non faccio un 
controllo sulla corretta sintassi del numero e non considero i negativi,
e' tanto per dare l'idea)
void ToFrac(char *s, int & num, int & den)
{
    bool dotfound = false;
    num=0; den=1; // init
    for ( ; *s; ++s)
    {
      if (*s=='.') dotfound=true;
      else
      if (*s>='0' && *s<='9')
      {
        num = 10*num+(*s-'0')
        if (dotfount) den*=10;
      }
    }
}
In questo modo inserisci un frazione esattamente uguale
al numero che l'utente voleva inserire ( a meno che l'utente
non abbia inserito un numero con 11 cifre, nel qual caso...sei fregato)
Caso 2) (numeri decimali ottenuti da operazioni "interne").
Come ti ho fatto vedere frazioni molto
semplici tipo (2.0/3.0) producono numeri che utilizzano
tutte e 14 o 15 o 16 le cifre significative
del numero decimale. A quel punto tu dovresti
1) usare una frazione del tipo 666666666666/10000000000 che non e'
nemmeno rappresentabile con gli interi (che anche unsigned arrivano
al massimo a circa 4 miliardi) e dovresti come minimo usare gli interi
a 64 bit (a seconda del tuo compilatore), ma sarebbe solo un palliativo,
perche' andando a moltiplicare due frazioni tipo (1.0/7.0)*(1.0/6.0) ti 
ritroveresti in pratica a sommare qualcosa tipo
1428571428571429/10000000000000000 * 666666666666667/10000000000000
che per essere rappresentato come frazione sotto avrebbe un numero
tipo 10^(30) che richiede circa 100 bit...
2) Cercare di dedurre periodo e antiperiodo dalle cifre decimali.
Se questo e' (apparentemente) possibile con numeri come 0.6666666666
prova a considerare casi come 1/19 : il periodo ha 18 cifre, quindi
e' escluso che tu lo possa "riconoscere" lavorando con i double.
3) Utilizzare un approccio piu' sofisticato: dato un numero decimale,
trovare la frazione che lo approssima meglio tra tutte quelle con
complessivamente (num+den) un certo numero massimo di cifre.
Oppure  trovare la frazione p/q che approssima meglio x
in modo da soddisfare una relazione del tipo |x-p/q| < c/(q^2) dove
c e' una opportuna piccola costante.
Questo approccio ti potrebbe dare effettivamente dei buoni
risultati (per esempio riconoscerebbe 1/19 anche dalle sue prime 14
cifre decimali). E' basato sulla rappresentazione del numero
mediante frazioni continue e va implementato con una certa attenzione.
(Domanda: ne vale la pena ?)
Indipendentemente da quello che tu vuoi fare, un limite essenziale
di qualunque approccio basato sulle frazioni e' il fatto che stai
usando dei numeri interi per rappresentare numeratore e denominatore.
Anche se dopo ogni calcolo normalizzi i numeri riconducendoti ad una
frazione p/q dove p e q sono primi tra loro) bastano poche operazioni
per mandare in overflow i numeri interi, per esempio
(1/2000)+(1/2001)+(1/2003) ha come denominatore 8016006000
che eccede 2 alla 32.
Quindi bisogna capire se questo progetto che stai portando avanti
avra' come target operazioni con le frazioni come quelle
che si danno ai bambini a scuola (tipo (1/3)+2-(2/5)) in cui
le frazioni sono ragionevolmente limitate, oppure se aspira
ad essere utilizzato in modo piu' generale.
In quel caso dovresti rivolgerti alla rappresentazione di numeri
in precisione arbitraria. Per esempio una libreria che fa questo
e' GMP che appunto implementa gia' interi, frazioni e numeri floating
in precisione arbitraria. Assolutamente da sconsigliare il tentare
di scriversi una simile libreria da soli, a meno di non essere
particolarmente scafati e avere un sacco di tempo da buttare.
L'ultima volta che la ho usata la GMP era in C, ma puo' darsi
che qualcuno abbia scritto degli involucri in c++ per essa o
possono esserci anche altre librerie gia' in c++.
Tu ci hai detto piu' o meno cosa dovrebbe fare il programma, ma
non ci hai detto quale e' lo SCOPO del programma (cioe' in
particolare se trattasi di qualcosa di serio a scopo lavorativo o
un esercizio di programmazione fine a se stesso) quindi e'
difficile capire quale dovrebbe essere la strada da seguire.
Se questo tuo e' un esercizietto e' ovvio che usare GMP o simili
potrebbe non avere senso, visto che implementa gia' la frazioni, se 
invece e' una cosa seria allora ti consiglio di farci un pensierino (la
GMP e' della gnu, quindi utilizzabile piu' o meno liberamente).
ciao,
g.