Curiosità numeri normali e numeri con virgola
salve a tutti sono un nuovo menbro del forum.
scusatemi per la banalità della domanda che e la seguente:
come si fa a capire se una variabile contiene un numero intero normale o un numero con virgola?
esempio:
x=a/b o x=dato immesso dall'utente
come faccio a capire se x (in ambo i casi) e un numero intero o un numero con virgola?
ringrazio anticipatamente chiunque sia in grado di darmi una risposta.
scusatemi per la banalità della domanda che e la seguente:
come si fa a capire se una variabile contiene un numero intero normale o un numero con virgola?
esempio:
x=a/b o x=dato immesso dall'utente
come faccio a capire se x (in ambo i casi) e un numero intero o un numero con virgola?
ringrazio anticipatamente chiunque sia in grado di darmi una risposta.
Risposte
Salve GundamRX91,
quindi 10.6 è in virgola mobile, giusto? E se avessi inserito da tastiera solamente il valore 10, secondo te avrebbe soddisfatto la condizione di essere in virgola mobile? Se si, allora mi trovi un numero reale che non soddisfi la condizione...
Cordiali saluti
"GundamRX91":
La funzione Int(x) restituisce la parte intera di un numero in virgola mobile: Int(10.6)=10
quindi 10.6 è in virgola mobile, giusto? E se avessi inserito da tastiera solamente il valore 10, secondo te avrebbe soddisfatto la condizione di essere in virgola mobile? Se si, allora mi trovi un numero reale che non soddisfi la condizione...


Cordiali saluti
e un problema perchè :
1) più il numero e grande più la procedura sarà lenta
2) dovrei fare tre conversioni una numero originale stringa due parte intera (stringa) in numero tre parte virgola (stringa) in numero e le conversioni da stringa a numero sono un pò pesantucce.
ci vorrebbe qualcosa di più veloce
1) più il numero e grande più la procedura sarà lenta
2) dovrei fare tre conversioni una numero originale stringa due parte intera (stringa) in numero tre parte virgola (stringa) in numero e le conversioni da stringa a numero sono un pò pesantucce.
ci vorrebbe qualcosa di più veloce
Comunque ok, ora il problema comincia ad aver senso.
In C/C++, nella libreria math.h (o cmath se preferisci) c'è la funzione modf http://www.cplusplus.com/reference/clibrary/cmath/modf/
In C/C++, nella libreria math.h (o cmath se preferisci) c'è la funzione modf http://www.cplusplus.com/reference/clibrary/cmath/modf/
la funzione va benissimo al problema
sapresti per caso dove posso trovare l'algoritmo di questa funzione cosi poi potrei tradurlo in assembler.
perchè, pultroppo, senza l'algoritmo mi ritrovo al punto di partenza.
sapresti per caso dove posso trovare l'algoritmo di questa funzione cosi poi potrei tradurlo in assembler.
perchè, pultroppo, senza l'algoritmo mi ritrovo al punto di partenza.
Ho controllato e sembra basti
IntegerPart = (int) x;
FracPart = x - IntegerPart;
Funziona anche con i segni. Almeno su Visual Studio 11... che tra l'altro è beta.
Comunque puoi provare a vedere che assembly genera il tuo compilatore.
IntegerPart = (int) x;
FracPart = x - IntegerPart;
Funziona anche con i segni. Almeno su Visual Studio 11... che tra l'altro è beta.
Comunque puoi provare a vedere che assembly genera il tuo compilatore.
Anche se per numeri grandi hai bisogno di sporcarti le mani molto di più e lavorare con le maschere di bit.
non è un problema di sporcarsi le mani perchè se fosse cosi mi fiderei dei compilatori.
e non scriverei in assembler
il problema e che gli algoritmi devono essere veloci e semplici più complichi l'algoritmo e più problemi hai.
e non scriverei in assembler
il problema e che gli algoritmi devono essere veloci e semplici più complichi l'algoritmo e più problemi hai.
Si scrive assembly, non assembler. In ogni caso evidenzierei che se non hai idea di come si fa, allora la funzione standard farà probabilmente meglio di qualsiasi funzione assembly che scriverai. Senza considerare che probabilmente il tuo programma avrà parti che necessitano di maggiore attenzione da parte tua di questa.
Io non mi preoccuperei troppo del compilatore, che generalmente permette di creare codice altamente ottimizzato, e se usi il C/C++ con le librerie di base stai sicuro che il relativo codice binario è "fatto" bene. Poi se vuoi farlo in assembly per il gusto di farlo, ok ma sicuramente hai molto da fare.
"garnak.olegovitc":
Salve GundamRX91,
[quote="GundamRX91"]
La funzione Int(x) restituisce la parte intera di un numero in virgola mobile: Int(10.6)=10
quindi 10.6 è in virgola mobile, giusto? E se avessi inserito da tastiera solamente il valore 10, secondo te avrebbe soddisfatto la condizione di essere in virgola mobile? Se si, allora mi trovi un numero reale che non soddisfi la condizione...


Cordiali saluti[/quote]
No, avrebbe detto che è un numero intero

"smalldragon":
e un problema perchè :
1) più il numero e grande più la procedura sarà lenta
Se il numero è grande è tutto inutile, i numeri in virgola mobile hanno un numero limitato di cifre significative, non hai nessun informazione su quale sia il valore intero esatto. Così, tanto per provare:
#include <iostream> template <typename T> T factorial(const int n) { T f = 1; for (int i = 1; i <= n; ++i) { f *= i; } return f; } int main() { int n = 14; long long a = factorial<long long>(n); // Esatto long long b = factorial<float>(n); // Approssimato std::cout << "14! = " << a << " (long long)" << '\n'; std::cout << "14! = " << b << " (float)" << '\n'; std::cout << "Differenza = " << (a - b) << '\n'; }
Risultato:
14! = 87178291200 (long long)
14! = 87178289152 (float)
Differenza = 2048
Come vedi il risultato usando un numero in virgola mobile float è solo approssimato. Non è preciso nemmeno alle migliaia, sui decimali non hai la minima informazione.
Quindi per numeri grandi non puoi sapere se sono interi o meno.
La cattiva notizia è che potresti non saperlo nemmeno per numeri piccoli. Ad esempio, 3.0000000000000000000000000000001 è ovviamente non intero, ma in un float non hai a disposizione tutte quelle cifre significative, quindi non potrai saperlo.
Quoto gli altri sul fatto che le funzioni della libreria standard sono abbastanza efficienti da non dover essere reimplementate (a meno che tu non abbia già fatto delle misurazioni che indichino che modf() è un collo di bottiglia).
OFF TOPIC: Questo topic appare per errore anche in amministrazione, ma la discussione continua in Informatica, grazie.
@claudio86: i numeri interi rappresentabili con un numero di bit inferiore al numero di bit della mantissa (che per un float dovrebbero essere se non ricordo male 24) sono perfettamente rappresentabili con un float. Il numero 3 in particolare dovrebbe essere uguale a $3F400000_16$ (ho ricostruito il numero a mano per cui potrei essermi sbagliato - ma è comunque rappresentabile come numero).
Tanto per una idea di come può essere implementato modf ne ho implementato una versione. Non sono sicuro però che sia completamente corretta
). Per numeri abbastanza normali se la cava discretamente. Rispetto al cast con l'int è comunque più preciso.

#include<cstdio> #include<cstdint> #include<cfloat> float modf_f(float x, float *intpart) { uint32_t * px = (uint32_t *) &x; uint32_t *pi = (uint32_t *) intpart; uint32_t const MANT_SHIFT = FLT_MANT_DIG - 1; uint32_t const EXP_MASK = (1 << (32 - FLT_MANT_DIG)) - 1; uint32_t const EXP_BIAS = FLT_MAX_EXP - 1; uint32_t const exp = (((*px) >> MANT_SHIFT) & EXP_MASK) - EXP_BIAS; if(exp <0) { *intpart = 0; return x; } else { uint32_t const EXP_SHIFT = MANT_SHIFT - exp; if(EXP_SHIFT < 0) { *intpart = x; return 0.0f; } else { *pi = *px & ~((1 << EXP_SHIFT)-1); return x - *intpart; } } } double modf_d(double x, double *intpart) { uint64_t * px = (uint64_t *) &x; uint64_t *pi = (uint64_t *) intpart; uint64_t const MANT_SHIFT = DBL_MANT_DIG - 1; uint64_t const EXP_MASK = (1 << (64 - DBL_MANT_DIG)) - 1; uint64_t const EXP_BIAS = DBL_MAX_EXP - 1; uint32_t const exp = (((*px) >> MANT_SHIFT) & EXP_MASK) - EXP_BIAS; if(exp <0) { *intpart = 0; return x; } else { uint32_t const EXP_SHIFT = MANT_SHIFT - exp; if(EXP_SHIFT < 0) { *intpart = x; return 0.0f; } else { *pi = *px & ~((1 << EXP_SHIFT)-1); return x - *intpart; } } }
"apatriarca":
@claudio86: i numeri interi rappresentabili con un numero di bit inferiore al numero di bit della mantissa (che per un float dovrebbero essere se non ricordo male 24) sono perfettamente rappresentabili con un float. Il numero 3 in particolare dovrebbe essere uguale a $3F400000_16$ (ho ricostruito il numero a mano per cui potrei essermi sbagliato - ma è comunque rappresentabile come numero).
Ma tu non lo sai se è intero. Avrai un algoritmo che restituisce un float e vorrai controllare se si tratta di un intero o meno. Ma se il risultato è quasi tre ma viene convertito in tre perché è il valore rappresentabile più vicino, o viceversa il risultato è tre ma per errori numerici diventa quasi tre, allora non puoi più fare questo controllo.
Ad esempio:
float seventh = 1.0f / 7.0f; float one = 1.0f; float almost_one = seventh + seventh + seventh; almost_one += seventh + seventh + seventh; almost_one += seventh; std::cout << "Differenza = " << std::setprecision(std::numeric_limits<float>::digits10) << (one - almost_one) << '\n';
Risultato:
Differenza = -1.19209e-007
Certo rispetto ai numeri grandi qua si tratta di errori molto più piccoli, quindi probabilmente il risultato approssimato è accettabile.
Per i numeri grandi invece il problema rimane.
[SP (Solo Per)]
il valore è $(40400000)_16$ hai sottratto $-1$ al Bias invece che aggiunger (o hai moltiplicato exp invece di dividerlo)
[/SP]
"apatriarca":
Il numero 3 in particolare dovrebbe essere uguale a $3F400000_16$ (ho ricostruito il numero a mano per cui potrei essermi sbagliato - ma è comunque rappresentabile come numero).
il valore è $(40400000)_16$ hai sottratto $-1$ al Bias invece che aggiunger (o hai moltiplicato exp invece di dividerlo)

[/SP]
@hamming_burst: Credo proprio di aver sottratto invece di aggiungerlo o viceversa.. Non ricordo che sono un po' andato a memoria..
@claudio86: Alcuni numeri possono essere rappresentati esattamente nella rappresentazione a virgola mobile e questi numeri sono definiti con precisione nello standard. Quel numero (nel senso la versione corretta) è esattamente 3, non una sua approssimazione.. E questo è vero per tutti gli interi sufficientemente piccoli. Ti invito a leggere una qualche descrizione dello standard..
@claudio86: Alcuni numeri possono essere rappresentati esattamente nella rappresentazione a virgola mobile e questi numeri sono definiti con precisione nello standard. Quel numero (nel senso la versione corretta) è esattamente 3, non una sua approssimazione.. E questo è vero per tutti gli interi sufficientemente piccoli. Ti invito a leggere una qualche descrizione dello standard..
sinceramento avevo postato qui la domanda
perchè mi aspettavo che c' era un sistema lineare o un teorema per capire se una variabile è un numero normale o con virgola.
allora se ho ben capito dovrò semplicemente fare l'operazione e metterla in una stringa e poi fare le conversioni opportune.
perchè in fondo e questo quello che fanno le funzioni del c o del c++.
perchè mi aspettavo che c' era un sistema lineare o un teorema per capire se una variabile è un numero normale o con virgola.
allora se ho ben capito dovrò semplicemente fare l'operazione e metterla in una stringa e poi fare le conversioni opportune.
perchè in fondo e questo quello che fanno le funzioni del c o del c++.
Ma chi ti ha detto che le funzioni del C fanno qualcosadi così stupido?! Facendo così fai in pratica due volte l'operazione che ti serve (in effetti in modo anche meno efficiente di quanto potenzialmente possibile) oltre a dover convertire da e verso codice ASCII. Il metodo corretto è quello di usare istruzioni assembly opportune direttamente (quando possibile ovviamente) oppure far finta che il numero sia un intero e usare le operazioni bit a bit per estrarre mantissa ed esponente.
"smalldragon":
sinceramento avevo postato qui la domanda
perchè mi aspettavo che c' era un sistema lineare o un teorema per capire se una variabile è un numero normale o con virgola.
allora se ho ben capito dovrò semplicemente fare l'operazione e metterla in una stringa e poi fare le conversioni opportune.
perchè in fondo e questo quello che fanno le funzioni del c o del c++.
Ma stai scherzando? Senza considerare che ti avevo messo anche un codice per modf... Te lo rimetto perché un po' cambiato, non dovrebbe essere difficile renderlo in asm. Penso che il codice delle librerie, se non è in asm, ha più o meno la forma presentata nel mio codice. Anche se probabilmente il tutto può essere reso più rapido usando asm e sse... Ma se è così sarà già stato fatto da qualcuno.
#include<cstdio> #include<cstdint> #include<cfloat> void IntPart32(uint32_t * const px, uint32_t *pi) { uint32_t const MANT_SHIFT = FLT_MANT_DIG - 1; // dovrebbe essere 23 uint32_t const EXP_MASK = (1 << (32 - FLT_MANT_DIG)) - 1; // dovrebbe essere 0xff uint32_t const EXP_BIAS = FLT_MAX_EXP - 1; // dovrebbe essere 0x7f uint32_t const exp = (((*px) >> MANT_SHIFT) & EXP_MASK) - EXP_BIAS; if(exp < 0) { *pi = 0; } else { uint32_t const EXP_SHIFT = MANT_SHIFT - exp; if(EXP_SHIFT < 0) { *pi = *px; } else { *pi = *px & ~((1 << EXP_SHIFT)-1); } } return; } void IntPart64(uint64_t * const px, uint64_t *pi) { uint64_t const MANT_SHIFT = DBL_MANT_DIG - 1; // dovrebbe essere 52 uint64_t const EXP_MASK = (1 << (64 - DBL_MANT_DIG)) - 1; //dovrebbe essere 0x7FF uint64_t const EXP_BIAS = DBL_MAX_EXP - 1; // dovrebbe essere 0x3ff uint32_t const exp = (((*px) >> MANT_SHIFT) & EXP_MASK) - EXP_BIAS; if(exp < 0) { *pi = 0; } else { uint32_t const EXP_SHIFT = MANT_SHIFT - exp; if(EXP_SHIFT < 0) { *pi = *px; } else { *pi = *px & ~((1 << EXP_SHIFT)-1); } } return; } float modf_float(float * const x, float *intpart) { IntPart32((uint32_t * const) x, (uint32_t *) intpart); return *x - *intpart; } double modf_double(double * const x, double *intpart) { IntPart64((uint64_t * const) x, (uint64_t *) intpart); return *x - *intpart; } long double modf_ldouble(long double * const x, long double *intpart) { IntPart64((uint64_t * const) x, (uint64_t *) intpart); // il compilatore che sto usando non possiede floating point da 80bit return *x - *intpart; }
Ho scritto in Visual Studio 11 per provarlo. Purtroppo non possiede floating point da 80bit (ho controllato!). Mi sarei divertito a farlo usando due puntatori per ogni long double (uno da 16 per esponente e segno e l'altro per la mantissa).
EDIT: c'è comunque un problema nel caso uno voglia ricavare solo la parte frazionaria e quindi metta a NULL il puntatore intpart... In quel caso la mia funzione semplicemente non funziona ed esce malamente. Per metterlo a posto bisognerebbe mettere qualche controllo sui puntatori. Un linguaggio più sicuro su questo aspetto d'altra parte non ti permetterebbe di giocare con i bit (a parte Ada forse
