Type casting C++

Super Squirrel
Ciao a tutti!

Se ho ben capito, posto float a(supponiamo a=2,36),la scrittura (int)a dovrebbe riportare la parte intera di a(nel caso specifico (int)a=2).
E' quindi possibile cambiare tipo ad un valore nel seguente modo:

float a=2.36;
int b;
b=(int)a;
//b sarà uguale a 2;


Se le premesse sono giuste perchè il seguente programma non funziona correttamente? (per esempio se inserisco 2.36 mi mostra 23599998)

#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{
float a;
int b;
cin>>a;
while((int)a!=a)a=a*10;
b=(int)a;
cout<<b<<endl;
system("PAUSE");
}

Risposte
vict85
Il problema è che un numero come \(0.00003\) non è rappresentato in maniera esatta come numero floating point. Quindi il risultato della moltiplicazione per \(10000\) non rende uguali le due espressioni.

Ti faccio un esempio con i decimali invece che con i numeri binari.
\(1/3 = 0,\overline{3}\)
se tu ti fermi alla terza cifra dopo la virgola avrai
\(\displaystyle 1/3 \approx 0,333 \)
quel numero moltiplicato per tre è uguale a \(\displaystyle 0,999\neq 1 \). Quindi il tuo codice lo moltiplicherebbe per \(\displaystyle 3 \) rendendolo \(\displaystyle 3 \) (per via delle approssimazioni e dei tre soli numeri dopo la virgola).

Super Squirrel
Ho capito il problema e ho fatto qualche ricerca più approfondita al riguardo,ma nonostante ciò non riesco a trovare una soluzione.
In pratica mi servirebbe trasformare un numero con la virgola(float) inserito da tastiera nella relativa frazione per poi inserire num e den in due variabili di tipo int.
Vorrei evitare di far inserire da tastiera parte intera e parte decimale mettendole in due variabili di tipo int per poi ricostruire la frazione(per esempio a=34, b=546, c=34546/1000).
Qualche suggerimento?

vict85
Il modo migliore è implementare una funzione che approssima il numero floating point o a doppia precisione con una frazione. Il metodo più efficace penso possa essere usare la Sequenza di Farey o le frazioni continue.

http://it.wikipedia.org/wiki/Sequenza_di_Farey
http://it.wikipedia.org/wiki/Frazione_continua

Super Squirrel
Molto interessante dal punto di vista matematico, ma preferirei una soluzione più semplice.

Ho pensato ad una cosa del genere, potrebbe andare?

#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{
char v[20];
int a=0,b=1,d=1;
bool c=0;
cin>>v;
for(int i=19;i>=0;i--)
{
if(v[i]>47 && v[i]<58)
{
a=(a+((v[i]-'0')*b));
b=b*10;
if(c==0)d=d*10;
}
if(v[i]==46) c=1;
}
cout<<a<<"/"<<d;
cout<<endl<<endl;
system("PAUSE");
}


Se invece volessi fare la stessa cosa con la classe vector(sempre se conviene) come dovrebbe essere modificato il codice? Ho provato, ma la mia scarsa conoscenza della classe mi ha portato a risultati poco soddisfacenti.

Super Squirrel
Parecchie cose devono ancora essere ottimizzate, cmq il seguente codice permette di inserire da tastiera un numero come stringa e lo trasforma nella corrispondente frazione con num e den allocati in variabili int (per esempio se inserisco 33/-0.22 mostra -150 con num=-150 e den=1)

#include <iostream>
#include <stdlib.h>

using namespace std;

int mcd(int a,int b)
{
if(a<0)a=-a;
int r=a%b;
while(r!=0)
{
a=b;
b=r;
r=a%b;
}
return b;
}

void normfrazione(int &a, int &b)
{
if(a==0)b=1;
else
{
if(b<0)
{
a=-a;
b=-b;
}
int c=mcd(a,b);
a=a/c;
b=b/c;
}
return;
}

void outfrazione(int a,int b)
{
cout<<a;
if(b!=1)cout<<"/"<<b;
return;
}

void moltfrazione(int &a,int &b,int c, int d)
{
a=a*c;
b=b*d;
normfrazione(a,b);
return;
}

int main()
{
char v[20];
int a=0,b=1,c=1,d=1,p=1,m;
bool x=0,y=0;
cin>>v;
for(int i=19;i>=0;i--)
{
if(v[i]>47 && v[i]<58)
{
a=(a+((v[i]-'0')*p));
p=p*10;
if(x==0)b=b*10;
}
if(v[i]==46)x=1;
if(y==0)
{
if(v[i]==47)
{
if(x==0)b=1;
x=0;
y=1;
m=i;
c=a;
d=b;
a=0;
b=1;
p=1;
}
}
if(i==0)
{
if(x==0)b=1;
}
}
if(v[0]==45)a=-a;
if(y==1)
{
if(v[m+1]==45)c=-c;
}
moltfrazione(a,b,d,c);
outfrazione(a,b);
cout<<endl<<endl;
system("PAUSE");
}


vale la pena ottimizzarlo o ci potrebbero essere controindicazioni per un'acquisizione-dati del genere?

EDIT: Codice semplificato

Super Squirrel
Codice ulteriormente semplificato:

#include <iostream>
#include <stdlib.h>

using namespace std;

int mcd(int a,int b)
{
if(a<0)a=-a;
int r=a%b;
while(r!=0)
{
a=b;
b=r;
r=a%b;
}
return b;
}

void normfrazione(int &a, int &b)
{
if(a==0)b=1;
else
{
if(b<0)
{
a=-a;
b=-b;
}
int c=mcd(a,b);
a=a/c;
b=b/c;
}
return;
}

void outfrazione(int a,int b)
{
cout<<a;
if(b!=1)cout<<"/"<<b;
return;
}

void moltfrazione(int &a,int &b,int c, int d)
{
a=a*c;
b=b*d;
normfrazione(a,b);
return;
}

int main()
{
char v[20];
int a=0,b=1,c=1,d=1,p=1;
cin>>v;
for(int i=19;i>=0;i--)
{
if(v[i]>47 && v[i]<58)
{
a=(a+((v[i]-'0')*p));
p=p*10;
}
if(v[i]==45)a=-a;
if(v[i]==46)b=p;
if(v[i]==47)
{
c=a;
d=b;
a=0;
b=1;
p=1;
}
}
moltfrazione(a,b,d,c);
outfrazione(a,b);
cout<<endl<<endl;
system("PAUSE");
}


Vorrei sapere se un'acquisizione-dati del genere possa causare problemi e se sia consigliabile aggiungere qualche controllo.

claudio862
Qualche commento.

Guarda la tua tastiera. C'è un motivo per cui il tasto più grande è lo spazio: dovrebbe essere quello usato più spesso. Usa lo spazio per rendere il codice più leggibile, separando operatori, variabili e funzioni. E soprattutto indenta il codice!
Ecco come potresti aver fatto:
#include <iostream>
#include <stdlib.h>

using namespace std;

int mcd(int a, int b)
{
    if (a < 0)
    {
        a = -a;
    }
    int r = a % b;
    while (r != 0)
    {
        a = b;
        b = r;
        r = a % b;
    }
    return b;
}

void normfrazione(int &a, int &b)
{
    if (a == 0)
    {
        b = 1;
    }
    else
    {
        if (b < 0)
        {
            a = -a;
            b = -b;
        }
        int c = mcd(a, b);
        a = a / c;
        b = b / c;
    }
    return;
}

void outfrazione(int a, int b)
{
    cout << a;
    if (b != 1)
    {
        cout << "/" << b;
    }
    return;
}

void moltfrazione(int &a, int &b, int c, int d)
{
    a = a * c;
    b = b * d;
    normfrazione(a, b);
    return;
}

int main()
{
    char v[20];
    int a = 0, b = 1, c = 1, d = 1, p = 1;
    cin >> v;
    for (int i = 19; i >= 0; i--)
    {
        if (v[i] > 47 && v[i] < 58)
        {
            a = (a + ((v[i] - '0') * p));
            p = p * 10;
        }
        if (v[i] == 45)
        {
            a = -a;
        }
        if (v[i] == 46)
        {
            b = p;
        }
        if (v[i] == 47)
        {
            c = a;
            d = b;
            a = 0;
            b = 1;
            p = 1;
        }
    }
    moltfrazione(a, b, d, c);
    outfrazione(a, b);
    cout << endl << endl;
    system("PAUSE");
}



Per motivi analoghi, racchiudi sempre il corpo di un if, for, while… tra parentesi graffe. Non scrivere
if(v[i]==45)a=-a;
ma scrivi
if (v[i] == 45)
{
    a = -a;
}



Dimenticati di system("PAUSE"). Piuttosto configura il tuo IDE per lasciare aperta la console quando il programma termina (non usare DevC++).


Gli header C (come ) in C++ si includono togliendo il .h e aggiungendo una c all'inizio: . Ma tanto quello ti serviva solo per system(), quindi toglilo direttamente.


Cerca di non usare using namespace std. I namespace esistono per evitare collisioni nei nomi, in questo modo li rendi inutili. Meglio scrivere il nome completo (std::cout, std::cin, std::endl…), almeno per namespace corti (std sono solo 3 lettere). Non usare mai using namespace std in un file header.


Usa nomi più significativi per variabili e funzioni. normfrazione, outfrazione… Molto meglio normalizza_funzione, stampa_funzione (o normalizzaFunzione, stampaFunzione, a seconda di come preferisci). a, b… Molto meglio numeratore, denominatore (o num, den).
Poi, in generale, meglio scrivere codice in inglese. Il linguaggio, la libreria standard e praticamente tutte le librerie di terze parti sono in inglese, stona molto avere funzioni o variabili in italiano.


Le funzioni che operano sulle frazioni sono scritte abbastanza bene, e soprattutto sono modulari. mcd() calcola l'mcd di due numeri. normfrazione() normalizza una frazione, chiamando mcd(). moltfrazione() moltiplica due frazioni, chiamando normfrazione() per normalizzare il risultato. È abbastanza raro vedere un principiante riuscire a dividere bene le responsabilità tra funzioni, complimenti!


La funzione main() invece no… Avresti dovuto scrivere una funzione leggifrazione().


Cosa sono 58, 45, 46, 47? In gergo, questi vengono chiamati magic numbers. Tu sai che sono i valori di : - . / nel codice ASCII, ma un'altra persona che legge il codice (o più probabilmente tu stesso tra qualche giorno) potrebbe non saperlo. Inoltre non tutte le implementazioni di C++ usano ASCII (ok, probabilmente tutte quelle che userai sì, però in linea teorica…).
Scrivi semplicemente ':', '-', '.', '/'.


La lettura dei due numeri funziona solo se l'utente scrive correttamente, inoltre si aspetta esattamente l'espressione NUMERO/NUMERO.
Sarebbe meglio avere una funzione che legge un numero, e una che legge la riga.

Ad esempio:
- leggi una riga in una stringa
- spezza la stringa in due quando trovi '/'
- leggi i due numeri separatamente.

La funzione per leggere un numero potrebbe essere:
- Leggi un eventuale '-' all'inizio.
- Se tutti i caratteri della stringa sono cifre, è un intero, leggi quell'intero.
- Se uno dei caratteri è '.', allora è una frazione. Leggi i due gruppi di cifre. Calcola la lunghezza del secondo gruppo (10 elevato a questa lunghezza è il denominatore). Concatena i due gruppi di cifre (questo intero è il numeratore).
Esempio: 1234.555 => dopo il punto ci sono 3 cifre, quindi il denominatore è 10^3 = 1000. Il numeratore è 1234555. Il risultato è 1234555 / 1000 = 246911 / 200
(se vuoi, considera che il '.' potrebbe essere all'inizio o alla fine, es. .5 = 0.5 e 12. = 12)
- Se ci sono altri caratteri, errore.

Oppure cerca di adattare quello che hai scritto tu.


Non mettere return; alla fine di funzioni void. Non serve.


Non usare char[]. Usa std::vector. O, ancora meglio, std::string. Per leggere un'intera linea:
std::string line;
std::getline(std::cin, line);


Infine, ricordati di questo sito.

vict85
Questo è un esempio di programma funzionante (almeno sembra funzionare) che legge una serie di numeri e li inserisce dentro un std::vector di frazioni. Ho usato un algoritmo ricorsivo e l'uso di std::string è un po' C-like.

#include <cctype>
#include <iostream>
#include <cstdint>
#include <string>
#include <vector>

typedef int64_t i64;

struct frac {
	i64 num, den;
};

void leggi_frazione(char const b[], int dim, std::vector<frac> &v);
void leggi_frazione2(char const b[], int dim, std::vector<frac> &v, frac &f, i64 sgn, i64 fs, i64 ds);

int main()
{
	std::cout << "Inserisci le frazioni separati da spazi in uno dei seguenti modi: " << std::endl;
	std::cout << "1) Numero intero   es.: 1234  " << std::endl;
	std::cout << "2) Frazione        es.: 12/5 " << std::endl;
	std::cout << "3) numero decimale es.: 12.34" << std::endl;
	std::cout << "L'inserimento di caratteri diversi da quelli indicati terminera' la lettura " << std::endl;

	std::string s;
	std::getline(std::cin, s);
	std::vector<frac> v;
	leggi_frazione(s.c_str(), s.size(), v);

	std::cout << "Valori letti : " << std::endl;
	for (frac f : v)
	{
		std::cout << f.num << '/' << f.den << ' ';
	}
	std::cout << std::endl;
}

void leggi_frazione(char const b[], int dim, std::vector<frac> &v)
{
	frac f = frac{ 0, 1 };
	
	// caso banale
	if (dim <= 0)
		return;

	// variabili di stato
	i64 sgn = 1;
	i64 frac_state = 0;
	i64 dec_state = 0;
	
	char const c = b[0];
	
	// considera il segno
	if (c == '-')
	{
		sgn = -1;
		leggi_frazione2(b + 1, dim - 1, v, f, sgn, frac_state, dec_state);
	}
	// ignora spazi iniziali
	else if (isspace(c))
	{
		leggi_frazione(b + 1, dim - 1, v);
	}
	// considera il caso in cui ci sia subito / o .
	else if (ispunct(c))
	{
		if (c == '/')
		{
			f.den = 0;
			frac_state = 1;
			leggi_frazione2(b + 1, dim - 1, v, f, sgn, frac_state, dec_state);
		}
		else if (c == '.')
		{
			dec_state = 1;
			leggi_frazione2(b + 1, dim - 1, v, f, sgn, frac_state, dec_state);
		}
	}
	// caso standard del numero
	else if (isdigit(c))
	{
		f.num = c - '0';
		leggi_frazione2(b + 1, dim - 1, v, f, sgn, frac_state, dec_state);
	}

	// caso di default non segnato in cui ritorna 0/1
}

void leggi_frazione2(char const b[], int dim, std::vector<frac> &v, frac &f, i64 sgn, i64 fs, i64 ds)
{
	// nel caso si sia concluso
	if (dim <= 0)
	{
		f.num *= sgn;
		if (f.den == 0)
			f.den = 1;
		v.push_back(f);
		return;
	}

	char const c = b[0];

	// modo alternativo per concludere
	if (isspace(c))
	{
		f.num *= sgn;
		if (f.den == 0)
			f.den = 1;
		v.push_back(f);
		leggi_frazione(b + 1, dim - 1, v);
	}
	// considera il caso in cui ci sia / o . 
	else if (ispunct(c))
	{
		if (c == '/')
		{
			if (fs != 0 || ds != 0) // ERRORE DI INSERIMENTO
			{
				f.num *= sgn;
				if (f.den == 0)
					f.den = 1;
				v.push_back(f);
				return;
			}
			else
			{
				fs = 1;
				f.den = 0;
				leggi_frazione2(b + 1, dim - 1, v, f, sgn, fs, ds);
			}			
		}
		else if (c == '.')
		{
			if (fs != 0 || ds != 0) // ERRORE DI INSERIMENTO
			{
				f.num *= sgn;
				if (f.den == 0)
					f.den = 1;
				v.push_back(f);
				return;
			}
			else
			{
				ds = 1;
				leggi_frazione2(b + 1, dim - 1, v, f, sgn, fs, ds);
			}
		}
	}
	// caso standard del numero
	else if (isdigit(c))
	{
		if (fs == 0)
		{
			f.num *= 10;
			f.num += c - '0';
		}
		else
		{
			f.den *= 10;
			f.den += c - '0';
		}
		
		if (ds != 0)
		{
			f.den *= 10;
		}
		leggi_frazione2(b + 1, dim - 1, v, f, sgn, fs, ds);
	}
	else
	{
		f.num *= sgn;
		if (f.den == 0)
			f.den = 1;
		v.push_back(f);
	}

}


Nota che lo stesso codice poteva essere fatto senza leggere la riga intera ma un carattere alla volta. Il codice non diventa molto diverso.

Super Squirrel
@claudio86

Grazie dei consigli!
Ho cominciato a programmare per un esamino a scelta fatto all'uni e sono rimasto colpito dall'impianto logico dei linguaggi di programmazione; quindi a volte mi diverto a fare qualche programmino nonostante sia consapevole della mia superficialità nei confronti del codice e delle mie scarse conoscenze in quest'ambito.
Cmq capisco tutto il discorso sulla leggibilità e sulla chiarezza del codice, quindi da ora in poi seguirò le tue dritte.

Per esempio ecco il programma sul rango e/o determinante di una matrice in cui ho aggiunto il sottoprogramma per l'acquisizione di un elemento della matrice a partire da una stringa:

#include <iostream>
#include <cstdint>
#include <string>

int64_t mcd(int64_t a, int64_t b)
{
    if(a < 0)
    {
        a = -a;
    }
    int64_t r = a % b;
    while(r != 0)
    {
        a = b;
        b = r;
        r = a % b;
    }
    return b;
}

void normalizza_frazione(int64_t &num, int64_t &den)
{
    if(num == 0)
    {
        den = 1;
    }
    else
    {
        if(den < 0)
        {
            num = -num;
            den = -den;
        }
        int64_t c = mcd(num, den);
        num = num / c;
        den = den / c;
    }
}

void prodotto_frazione(int64_t &num_1, int64_t &den_1, int64_t num_2, int64_t den_2)
{
    num_1 = num_1 * num_2;
    den_1 = den_1 * den_2;
    normalizza_frazione(num_1, den_1);
}

void inserisci_frazione(int64_t &num, int64_t &den)
{
    num = 0;
    den = 1;
    int64_t a = 1, b = 1, c = 1;
    std::string v;
    std::cin >> v;
    for(int64_t i = v.size(); i > -1; i--)
    {
        if(v[i] > '/' && v[i] < ':')
        {
            num = num + (v[i] - '0') * c;
            c = c * 10;
        }
        if(v[i] == '-')
        {
            num = -num;
        }
        if(v[i] == '.')
        {
            den = c;
        }
        if(v[i] == '/')
        {
            a = num;
            b = den;
            num = 0;
            den = 1;
            c = 1;
        }
    }
    prodotto_frazione(num, den, b, a);
}

void mostra_frazione(int64_t num, int64_t den)
{
    normalizza_frazione(num, den);
    if(den == 1)
    {
    std::cout << num << "\t";
    }
    else
    {
    std::cout << num << "/" << den << "\t";
    }
}

void somma_frazione(int64_t &num_1, int64_t &den_1, int64_t num_2, int64_t den_2)
{
    int64_t e, f;
    normalizza_frazione(num_1, den_1);
    normalizza_frazione(num_2, den_2);
    f = den_1 * den_2 / mcd(den_1, den_2);
    e = f / den_1 * num_1 + f / den_2 * num_2;
    normalizza_frazione(e, f);
    num_1 = e;
    den_1 = f;
}

void scambia_frazione(int64_t &num_1, int64_t &den_1, int64_t &num_2, int64_t &den_2)
{
    int64_t e;
    e = num_1;
    num_1 = num_2;
    num_2 = e;
    e = den_1;
    den_1 = den_2;
    den_2 = e;
}

int main()
{
    int rig, col, rango = 0, x = -1, y = 0, z = 1;
    int64_t a, b, c, d, v[20][20][2];
    std::cout << "** RANGO E DETERMINANTE DI UNA MATRICE **";
    std::cout << std::endl << std::endl << "Numero di righe: ";
    std::cin >> rig;
    std::cout << "Numero di colonne: ";
    std::cin >> col;
    for(int i = 0; i < rig; i++)
    {
        std::cout << std::endl;
        for(int j = 0; j < col; j++)
        {
            std::cout << "v" << "[" << i << "," << j << "] = ";
            inserisci_frazione(v[i][j][0], v[i][j][1]);
        }
    }
    if(rig > col)
    {
        for(int i = 0; i < col; i++)
        {
            for(int j = 0; j < col; j++)
            {
               if (j > i)
               {
                   scambia_frazione(v[i][j][0], v[i][j][1], v[j][i][0], v[j][i][1]);
               }
            }
        }
        for(int i = col; i < rig; i++)
        {
           for(int j = 0; j < col; j++)
           {
               scambia_frazione(v[i][j][0], v[i][j][1], v[j][i][0], v[j][i][1]);
           }
        }
        a = rig;
        rig = col;
        col = a;
    }
    if(rig > 1)
    {
        while(rig - y > 1)
        {
            for(int i = y; i < rig; i++)
            {
                if(v[i][y][0] != 0)
                {
                    x = i;
                    break;
                }
            }
            if(x == -1)
            {
                y++;
            }
            else
            {
                if(x > y)
                {
                    for(int j = 0; j < col; j++)
                    {
                        scambia_frazione(v[y][j][0], v[y][j][1], v[x][j][0], v[x][j][1]);
                    }
                    z = -z;
                }
                for(int i = y; i < rig - 1; i++)
                {
                    if(v[i+1][y][0] != 0)
                    {
                        a = v[i+1][y][0];
                        b = v[i+1][y][1];
                        prodotto_frazione(a, b, v[y][y][1], v[y][y][0]);
                        a = -a;
                        for(int j = y; j < col; j++)
                        {
                            c = a;
                            d = b;
                            prodotto_frazione(c, d, v[y][j][0], v[y][j][1]);
                            somma_frazione(v[i+1][j][0], v[i+1][j][1], c, d);
                        }
                    }
                }
                y++;
                rango++;
                x = -1;
            }
        }
        for(int j = y; j < col; j++)
        {
            if(v[y][j][0] != 0)
            {
                rango++;
                break;
            }
        }
    }
    if(rig == 1)
    {
        for(int j = 0; j < col; j++)
        {
            if(v[0][j][0] != 0)
            {
                rango++;
                break;
            }
        }
    }
    std::cout << std::endl << "Matrice ridotta a scalini" << std::endl;
    for(int i = 0; i < rig; i++)
    {
        std::cout << std::endl;
        for(int j = 0; j < col; j++)
        {
            mostra_frazione(v[i][j][0], v[i][j][1]);
        }
    }
    std::cout << std::endl << std::endl << "Il rango della matrice vale: " << rango;
    if(rig == col)
    {
        if(rig == rango)
        {
            a = 1;
            b = 1;
            for(int i = 0; i < rig; i++)
            {
                prodotto_frazione(a, b, v[i][i][0], v[i][i][1]);
            }
            a = a * z;
            std::cout << std::endl << std::endl << "Il determinante della matrice vale: ";
            mostra_frazione(a, b);
        }
        else
        {
            std::cout << std::endl << std::endl << "La matrice ha determinante nullo";
        }
    }
    std::cout << std::endl;
}


Cmq volevo sapere, vista la mia ancora scarsa dimestichezza con la classe string, se nel sottoprogramma inserisci_frazione utilizzo bene tale classe. inoltre nel main conviene sostituire l'array con un vettore o in questo caso è inutile? (in ogni caso devo ancora approfondire la classe vector)

La lettura dei due numeri funziona solo se l'utente scrive correttamente, inoltre si aspetta esattamente l'espressione NUMERO/NUMERO.


devo ancora aggiungere alcuni controlli, cmq non capisco cosa intendi con "si aspetta esattamente l'espressione NUMERO/NUMERO"

@ vict85

Grazie, appena finisco di leggermi e comprendere per bene il codice ti faccio sapere.

Super Squirrel
Ho avuto alcune difficoltà a capire alcuni passaggi visto che non conosco struct e vector, cmq il programma sembra funzionare anche se i controlli sull'inserimento non sono molto curati.
per esempio
2/0 ----> 2/1
0/2 ---> 0/2
1/-2 ----> non lo legge
3// ---> 3/1
. ----> 0/1
2.3.4 --->23/10

mi scuso se ho frainteso io le finalità del programma

La lettura dei due numeri funziona solo se l'utente scrive correttamente, inoltre si aspetta esattamente l'espressione NUMERO/NUMERO.


l'algoritmo funziona sia per x/y (dove x e y possono essere interi o decimali, positivi o negativi) sia per x (intero o decimale, positivo o negativo).
ho aggiunto anche i controlli sull'inserimento(caratteri validi e loro limitazioni, divisione per 0, successione validi di caratteri validi...):

#include <iostream>
#include <cstdint>
#include <string>
#include <cstdlib>

int64_t mcd(int64_t a, int64_t b)
{
    if(a < 0)
    {
        a = -a;
    }
    int64_t r = a % b;
    while(r != 0)
    {
        a = b;
        b = r;
        r = a % b;
    }
    return b;
}

void normalizza_frazione(int64_t &num, int64_t &den)
{
    if(num == 0)
    {
        den = 1;
    }
    else
    {
        if(den < 0)
        {
            num = -num;
            den = -den;
        }
        int64_t c = mcd(num, den);
        num = num / c;
        den = den / c;
    }
}

void prodotto_frazione(int64_t &num_1, int64_t &den_1, int64_t num_2, int64_t den_2)
{
    num_1 = num_1 * num_2;
    den_1 = den_1 * den_2;
    normalizza_frazione(num_1, den_1);
}

void inserisci_frazione(int64_t &num, int64_t &den, bool &x)
{
    num = 0;
    den = 1;
    int64_t a = 1, b = 1, c = 1;
    int virgola = 0, meno = 0, fratto = 0, v_1, v_2, m_1, m_2, f;
    x = 1;
    std::string v;
    std::cin >> v;
    for(int i = 0; i < v.size(); i++)
    {
        if(v[i] < '-' || v[i] > '9')
        {
            x = 0;
            break;
        }
        if(v[i] == '.')
        {
            virgola++;
            if(virgola > 2)
            {
                x = 0;
                break;
            }
            if(virgola == 1)
            {
                v_1 = i;
            }
            else
            {
                v_2 = i;
            }
        }
        if(v[i] == '-')
        {
            meno++;
            if(meno > 2)
            {
                x = 0;
                break;
            }
            if(meno == 1)
            {
                m_1 = i;
            }
            else
            {
                m_2 = i;
            }
        }
        if(v[i] == '/')
        {
            fratto++;
            if(fratto > 1)
            {
                x = 0;
                break;
            }
            f = i;
        }
    }
    while(x == 1)
    {
        if(virgola > 0)
        {
            if(v[v_1 - 1] < '0' || v[v_1 + 1] < '0')
            {
                x = 0;
                break;
            }
            if(virgola == 2)
            {
                if(v[v_2 - 1] < '0' || v[v_2 + 1] < '0' || fratto == 0 )
                {
                    x = 0;
                    break;
                }
            }
        }
        if(meno > 0)
        {
            if(v[m_1 + 1] < '0')
            {
                x = 0;
                break;
            }
            if(meno == 2)
            {
                if(v[m_2 + 1] < '0' || fratto == 0)
                {
                    x = 0;
                    break;
                }
            }
        }
        if(fratto > 0)
        {
            if(v[f - 1] < '0')
            {
                x = 0;
                break;
            }
            if(virgola == 2)
            {
                if(v_1 > f || v_2 < f)
                {
                    x = 0;
                    break;
                }
            }
            if(meno == 2)
            {
                if(m_1 > f || m_2 < f)
                {
                    x = 0;
                    break;
                }
            }
        }
        break;
    }
    if(x == 1)
    {
       for(int i = v.size(); i > -1; i--)
       {
           if(v[i] > '/' && v[i] < ':')
           {
               num = num + (v[i] - '0') * c;
               c = c * 10;
           }
           if(v[i] == '-')
           {
               num = -num;
           }
           if(v[i] == '.')
           {
               den = c;
           }
           if(v[i] == '/')
           {
               a = num;
               b = den;
               num = 0;
               den = 1;
               c = 1;
           }
       }
       if(a != 0)
       {
           prodotto_frazione(num, den, b, a);
       }
       else
       {
           x = 0;
       }
    }
    if(x == 0)
    {
        std::cout << std::endl << "Inserimento non valido";
    }
}

void mostra_frazione(int64_t num, int64_t den)
{
    normalizza_frazione(num, den);
    if(den == 1)
    {
    std::cout << num << "\t";
    }
    else
    {
    std::cout << num << "/" << den << "\t";
    }
}

int main()
{
    int64_t a , b;
    bool x;
    std::cout << "Caratteri validi: . - / 0 1 2 3 4 5 6 7 8 9";
    std::cout << std::endl << std::endl << "Inserire valore: ";
    inserisci_frazione(a, b, x);
    if(x == 1)
    {
        std::cout << std::endl << "Frazione ridotta: ";
        mostra_frazione(a, b);
    }
    std::cout << std::endl <<std::endl;
    system("PAUSE");
}


credo di aver preso in considerazione ogni controllo sull'inserimento, in caso contrario ogni critica è ben accetta.

vict85
Ho dato per scontato che l'utente inserisse il - solo al numeratore e ho accolto le considerazioni di claudio86 sul fatto che /2 volesse essere un 1/2, che .2 fosse un 0.2 e che 2. fosse uguale a 2 . Il fatto che . fosse uguali a 0.0 è stato la conseguenza diretta. Il mio programma avrebbe potuto tranquillamente accogliere 2/0 ma pur di supporre che 2/ fosse uguale a 2 ho dovuto assicurarmi che non ponessi mai a 0 il denominatore (nel momento in cui veniva un / si deve porre a 0 il denominatore per assicurarsi che l'algoritmo ricorsivo funzioni).

In un certo senso ho scritto un algoritmo che cercasse di interpretare il risultato anche in caso di errore di inserimento, seppur in caso di situazioni non contemplate blocchi l'esecuzione del programma e inserisca nel vettore il numero generato finora. È possibilissimo modificare il programma in modo che inserisca il valore solo quando il numero è inserito correttamente.

P.S.: Se ho capito bene il tuo corso di programmazione è finito. Pertanto DISINSTALLA DEV-C++ e installa code::blocks, codelite oppure visual studio community 2013. Oppure ancora usa notepad++ (o altri editor di testo seri) insieme al terminale. Quel programma viene ancora usato solo perché i professori universitari sono pigri e non hanno voglia di installare code::blocks su tutti i pc dell'università. Ma è attualmente il peggio che puoi trovare sul “mercato”. Legato a questo elimina la riga system("PAUSE");

vict85
Ora è molto più restrittivo nell'accettare elementi. Seppur non presenti alcun codice che informa l'utente dell'errore.

#include <cctype>
#include <iostream>
#include <cstdint>
#include <string>
#include <vector>

typedef int64_t i64;

struct frac {
	i64 num, den;
};

void leggi_frazione(char const b[], std::vector<frac> &v);
void leggi_frazione2(char const b[], std::vector<frac> &v, frac &f, i64 sgn, i64 fs, i64 ds);

int main()
{
	std::cout << "Inserisci le frazioni separati da spazi in uno dei seguenti modi: " << std::endl;
	std::cout << "1) Numero intero   es.: 1234  " << std::endl;
	std::cout << "2) Frazione        es.: 12/5 " << std::endl;
	std::cout << "3) numero decimale es.: 12.34" << std::endl;
	std::cout << "L'inserimento di caratteri diversi da quelli indicati terminera' la lettura " << std::endl;

	std::string s;
	std::getline(std::cin, s);
	std::vector<frac> v;
	leggi_frazione(s.c_str(), v);

	std::cout << "Valori letti : " << std::endl;
	for (frac f : v)
	{
		std::cout << f.num << '/' << f.den << ' ';
	}
	std::cout << std::endl;
}

void leggi_frazione(char const b[], std::vector<frac> &v)
{
	frac f = frac{ 0, 1 };

	// variabili di stato
	i64 sgn = 1;
	i64 fs = 0;
	i64 ds = 0;

	char const c = b[0];

	// stringa terminata
	if (c == '\0')
		return;

	// considera il segno
	if (c == '-')
	{
		sgn = -1;
		char const c2 = b[1];
		if (isdigit(c2))
			leggi_frazione2(b + 2, v, f, sgn, fs, ds);
		else
			return;
	}
	// ignora spazi iniziali
	else if (isspace(c))
	{
		leggi_frazione(b + 1, v);
	}
	// caso standard del numero
	else if (isdigit(c))
	{
		f.num = c - '0';
		leggi_frazione2(b + 1, v, f, sgn, fs, ds);
	}
}

void leggi_frazione2(char const b[], std::vector<frac> &v, frac &f, i64 sgn, i64 fs, i64 ds)
{
	char const c = b[0];

	if (c == '\0')
	{
		f.num *= sgn;
		v.push_back(f);
		return;
	}

	// modo alternativo per concludere
	if (isspace(c))
	{
		f.num *= sgn;
		v.push_back(f);
		leggi_frazione(b + 1, v);
	}
	// considera il caso in cui ci sia / o . 
	else if (ispunct(c))
	{
		if (c == '/')
		{
			if (fs != 0 || ds != 0) // ERRORE DI INSERIMENTO
			{
				return;
			}
			else
			{
				fs = 1;
				char const c2 = b[1];
				if (isdigit(c2))
				{
					f.den = c2 - '0';
					leggi_frazione2(b + 2, v, f, sgn, fs, ds);
				}
				else if (c2 == '-')
				{
					sgn = -sgn;
					char const c3 = b[2];
					if (isdigit(c3))
					{
						f.den = c3 - '0';
						leggi_frazione2(b + 3, v, f, sgn, fs, ds);
					}
				}
				else
					return;
			}
		}
		else if (c == '.')
		{
			if (fs != 0 || ds != 0) // ERRORE DI INSERIMENTO
			{
				return;
			}
			else
			{
				ds = 1;
				char const c2 = b[1];
				if (isdigit(c2))
				{
					f.num *= 10;
					f.num += c2 - '0';
					f.den = 10;
					leggi_frazione2(b + 2, v, f, sgn, fs, ds);
				}
				else
					return;
				
			}
		}
	}
	// caso standard del numero
	else if (isdigit(c))
	{
		if (fs == 0)
		{
			f.num *= 10;
			f.num += c - '0';
		}
		else
		{
			f.den *= 10;
			f.den += c - '0';
		}

		if (ds != 0)
		{
			f.den *= 10;
		}
		leggi_frazione2(b + 1, v, f, sgn, fs, ds);
	}

}

Super Squirrel
P.S.: Se ho capito bene il tuo corso di programmazione è finito. Pertanto DISINSTALLA DEV-C++ e installa code::blocks, codelite oppure visual studio community 2013. Oppure ancora usa notepad++ (o altri editor di testo seri) insieme al terminale. Quel programma viene ancora usato solo perché i professori universitari sono pigri e non hanno voglia di installare code::blocks su tutti i pc dell'università. Ma è attualmente il peggio che puoi trovare sul “mercato”. Legato a questo elimina la riga system("PAUSE");


Si, allora il prof mi fece scaricare dev, ma da quando ho iniziato il programma sul rango e determinante di una matrice sto usando code::blocks ed effettivamente è tutta un'altra storia.
Per quanto riguarda la riga system("PAUSE"), se la tolgo e faccio partire il programma tramite code::blocks non ci sono problemi in quanto la chiusura del programma in questo caso non è automatica; se invece faccio partire l'exe direttamente(doppio clic sul file .exe per intenderci), la chiusura del programma è automatica.

Quindi se tolgo system("PAUSE") come posso impedire che il programma avviato direttamente dall'exe si chiuda??

vict85
Il modo più semplice consiste nell'aprire un terminale o una powershell e far partire il programma da lì. Altrimenti si può scrivere materialmente un codice equivalente o quasi (anche se spesso si finisce per usare conio.h che non è standard né portabile).

Super Squirrel
So che è molto poco elegante come cosa, ma potrebbe andare?
almeno così il programma sarebbe portabile e non ci sarebbe bisogno di aprirlo tramite code::block o prompt dei comandi.

#include <iostream>

int main()
{
    std::cout << "HELLO!" << std::endl;
    char a;
    std::cout << "INSERIRE UN CARATTERE PER USCIRE: ";
    std::cin >> a;
}

vict85
Può andare. Anche se cin.get() è meglio.

Vedi anche qui sul perché chiamare system non è una buona cosa:
http://www.gidnetwork.com/b-61.html

Comunque puoi anche creare un file .bat (o .ps1) che contiene
nome_file.exe
pause

e poi fai partire il bat invece che l'exe (il .ps1 potrebbe necessitare qualche operazione aggiuntiva per via delle sicurezze di windows). Chiama il programma pause dopo che la tua funzione ha concluso, ma non lo fa all'interno del tuo programma. Inoltre non modifichi in alcun modo il tuo programma massimizzando la portabilità. Di fatto code::blocks fa esattamente questo solo che gestisce lui questa operazione.

claudio862
Vorrei rispondere con più calma ad alcuni punti, ma purtroppo in questo periodo il tempo libero tende a zero. Magari ci tornerò tra qualche giorno.

Intanto faccio un commento veloce su system("PAUSE"). Probabilmente ti sembra più comodo e veloce fare doppio click sul programma invece che farlo partire da una console, però in realtà la console diventerà parecchio più versatile man mano che ti abituerai. Potrai redirigere l'output su un file, passare argomenti ai tuoi programmi, concatenare diversi comandi, usare altre utility di terze parti (es. pandoc, ffmpeg, imagemagick…), se passerai ad altri linguaggi potrai usare il loro REPL (anche se in realtà ci sono interpreti per il C++, vedi Cling).
Semplicemente, lancia le applicazioni console da una console (a parte quando le lanci direttamente da Code::Blocks / Visual Studio).

Obietterai che il prompt dei comandi di Windows è orrendo. Concordo. Prova Cmder.

vict85
"claudio86":
Obietterai che il prompt dei comandi di Windows è orrendo. Concordo. Prova Cmder.


Microsoft concorda con te, tanto che hanno creato la powershell (di cui non so molto). Sostanzialmente è una eredità troppo difficile da estirpare e sostituire che preferiscono non metterci le mani.

claudio862
[ot]
"vict85":
[quote="claudio86"]Obietterai che il prompt dei comandi di Windows è orrendo. Concordo. Prova Cmder.


Microsoft concorda con te, tanto che hanno creato la powershell (di cui non so molto). Sostanzialmente è una eredità troppo difficile da estirpare e sostituire che preferiscono non metterci le mani.[/quote]

Credo che con Powershell abbiano solo migliorato la shell, ma non il terminale. Un po' come su Linux puoi usare bash, bb, zsh, csh (python, ruby…) come shell, e Terminal, Konsole, xterm… come terminale. Cmder è più un terminale, come shell usa cmd o powershell (python, ruby… forse si riesce anche bash, io non ho intenzione di provarci).[/ot]

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.