Domanda

Mi sento come devo solo essere in grado di trovarlo. C'è qualche ragione che la funzione pow C ++ non implementa la funzione di "potere" per qualsiasi cosa, tranne floats e doubles?

So che l'implementazione è banale, mi sento come sto facendo un lavoro che dovrebbe essere in una libreria standard. Una robusta funzione di potenza (ossia maniglie troppo pieno in qualche modo esplicito coerente) non è divertente da scrivere.

È stato utile?

Soluzione

È un dato di fatto, lo fa.

Poiché C ++ 11 è un'implementazione su modelli di pow(int, int) --- ed anche casi più generali, vedere (7) in http://en.cppreference.com/w/cpp/numeric/math/ pow


EDIT: i puristi potrebbero obiettare questo non è corretto, in quanto v'è in realtà "promossa" tipizzazione utilizzato. Un modo o nell'altro, si ottiene un risultato corretto int, o un errore, sui parametri int.

Altri suggerimenti

A partire dal C++11, casi particolari sono stati aggiunti alla suite di funzioni di potenza (e non solo). Stati C++11 [c.math] /11, dopo aver elencato tutti i sovraccarichi float/double/long double (il corsivo è mio, e parafrasato):

  

Inoltre, vi sarà sovraccarichi supplementari sufficienti a garantire che, se qualsiasi argomento corrispondente ad un parametro double trovi tipo double o di tipo intero, quindi tutti gli argomenti corrispondenti ai parametri double sono effettivamente cast double.

Quindi, in sostanza, parametri interi sarà aggiornato a doppie per eseguire l'operazione.


Prima di C++11 (che era quando la tua domanda è stata fatta), non sovraccarichi interi esisteva.

Dato che non ero né strettamente associato con i creatori di CC++ nei giorni della loro creazione (anche se I am un po 'vecchia), nè parte dei comitati ANSI / ISO che hanno creato gli standard, questo è necessariamente parere da parte mia. Mi piacerebbe pensare che sia informato parere ma, come mia moglie vi dirà (frequentemente e senza molto incoraggiamento necessario), sono stato sbagliato prima: -)

Supposizione, per quello che vale, segue.

I sospettato che il motivo per l'originale C pre-ANSI non ha avuto questa caratteristica è perché è stato tutto inutile. In primo luogo, vi era già un modo perfetto di fare potenze intere (con doppie e poi semplicemente riconversione in un intero, il controllo di integer overflow e underflow prima della conversione).

In secondo luogo, un'altra cosa che dovete ricordare è che l'intento originale del C era come sistemi linguaggio di programmazione, ed è discutibile se virgola mobile è desiderabile in quel campo a tutti.

Da uno dei suoi casi d'uso iniziale era di codice UNIX, il virgola mobile sarebbe praticamente inutile. BCPL, su cui si basava C, aveva anche alcuna utilità per i poteri (non ha avuto in virgola mobile a tutti, a memoria).

  

Per inciso, un operatore di potenza integrato sarebbe stato probabilmente un operatore binario, piuttosto che una chiamata di libreria. Non si aggiunge due numeri interi con x = add (y, z) ma con x = y + z -. Parte del linguaggio corretto , piuttosto che la libreria

In terzo luogo, in quanto l'attuazione del potere integrale è relativamente banale, è quasi certo che gli sviluppatori del linguaggio sarebbe meglio utilizzare il loro tempo fornire roba più utile (vedi sotto commenti sul costo di opportunità).

che è rilevante per la C++ originale anche. Dal momento che l'implementazione originale era effettivamente solo un traduttore che ha prodotto il codice C, è riportato molti degli attributi di C. Il suo intento originale era di C-con-le classi, non è C-con-classi-plus-un-po-po-di-extra-math-stuff.

Per quanto riguarda il motivo per cui non è mai stata aggiunta alle norme prima C++11, si deve ricordare che le norme in fase di impostazione corpi hanno linee guida specifiche da seguire. Ad esempio, ANSI C era specificamente affidato il compito di codificare la prassi esistente, non per creare una nuova lingua. In caso contrario, potrebbero avere impazzito e dato noi Ada: -)

iterazioni successive di questo standard hanno anche linee guida specifiche e possono essere trovati nei documenti Razionale (razionale motivo per cui la commissione composta certe decisioni, non le motivazioni per il linguaggio stesso).

Ad esempio il documento C99 logica porta specificamente proposti due del C89 principi guida che limitano ciò che può essere aggiunto:

  • Mantenere la lingua piccolo e semplice.
  • Fornire un solo modo per fare un'operazione.

Linee guida (non necessariamente quelli specifica quelli) sono stabiliti per l'individuogruppi di lavoro e quindi di limitare le commissioni C++ (e tutti gli altri gruppi ISO) pure.

In aggiunta, gli standard di impostazione corpi si rendono conto che c'è un costo opportunità (un termine economico che significa quello che hai da rinunciare per una decisione presa) per ogni decisione che fanno. Ad esempio, il costo opportunità di acquisto che $ 10.000 macchina uber-gaming è cordiali rapporti (o probabilmente tutti relazioni) con l'altra metà per circa sei mesi.

Eric Gunnerson spiega bene con i suoi -100 punti spiegazione sul motivo per cui le cose non sono sempre aggiunti a Microsoft prodotti- fondamentalmente una funzione inizia 100 punti nel foro in modo da deve aggiungere un po 'di valore da nemmeno presa in considerazione.

In altre parole, preferireste avere un operatore di potenza integrato (che, onestamente, qualsiasi coder mezza decente potrebbe montare in minuti dieci) o multi-threading aggiunto allo standard? Per quanto mi riguarda, preferirei avere quest'ultimo e non hanno di letame in giro con le implementazioni diverse sotto UNIX e Windows.

Mi piacerebbe vedere anche migliaia e migliaia di collezione della libreria standard (hash, Usa Btree, alberi rosso-neri, dizionario, mappe arbitrarie e così via) così, ma, come dice Motivazione:

  

Uno standard è un trattato tra realizzatore e programmatore.

E il numero degli esecutori sugli organismi di normalizzazione superano di gran lunga il numero di programmatori (o almeno quei programmatori che non capiscono costo opportunità). Se è stata aggiunta tutta quella roba, la prossima C++ standard sarebbe C++215x e probabilmente sarebbe pienamente attuata dagli sviluppatori del compilatore trecento anni dopo.

In ogni caso, questo è il mio (piuttosto voluminosi) pensieri al riguardo. Se solo i voti sono stati distribuiti basi sulla quantità piuttosto che la qualità, mi piacerebbe presto saltare tutti gli altri fuori dall'acqua. Grazie per l'ascolto: -)

Per qualsiasi tipo a larghezza fissa integrale, quasi tutte le possibili coppie di ingressi di overflow del tipo, comunque. Qual è l'uso di standardizzare una funzione che non riconoscono un risultato utile per la stragrande maggioranza dei suoi possibili ingressi?

È praticamente necessità di avere un grande tipo intero al fine di rendere la funzione utile, e la maggior parte delle librerie di grandi interi forniscono la funzione.


Modifica In un commento sulla questione, static_rtti scrive "?. La maggior parte ingressi indurlo a trabocco Lo stesso vale per l'exp e doppio pow, non vedo nessuno lamentarsi" Questo non è corretto.

il permesso di Let parte exp, perché non è questo il punto (anche se sarebbe effettivamente rendere più forte il mio caso), e concentrarsi sulla double pow(double x, double y). Che parte di (x, y) coppie fa questa funzione qualcosa di utile (cioè, non semplicemente overflow o underflow)?

In realtà sto andando a concentrarsi solo su una piccola parte delle coppie di ingressi per i quali pow ha un senso, perché sarà sufficiente a dimostrare il mio punto: se x è positivo e | y | <= 1, quindi pow non trabocchi o underflow. Questo comprende circa un quarto di tutte le coppie in virgola mobile (esattamente la metà di non NaN numeri in virgola mobile sono positive, e poco meno della metà dei numeri a virgola mobile non NaN avere una grandezza inferiore a 1). Ovviamente, ci sono molto di altre coppie di ingressi per i quali pow produce risultati utili, ma abbiamo accertato che è almeno un quarto di tutti gli ingressi.

Ora andiamo sguardo ad una dimensione fissa (cioè non bignum) funzione di potenza intero. Per quello che gli ingressi parte non lo fa semplicemente troppo pieno? Per massimizzare il numero di coppie di input significativi, la base deve essere firmato e l'esponente senza segno. Supponiamo che la base e l'esponente sono entrambi i bit n larghezza. Siamo in grado di ottenere facilmente un balzo sulla porzione di input che sono significative:

  • Se l'esponente 0 o 1, quindi qualsiasi base è significativa.
  • Se l'esponente è 2 o maggiore, allora nessuna base maggiore di 2 ^ (n / 2) produce un risultato significativo.

Così, delle coppie di ingressi 2 ^ (2n), inferiore a 2 ^ (n + 1) + 2 ^ (3n / 2) producono risultati significativi. Se guardiamo a quello che è probabilmente il maggior numero di interi uso comune, a 32 bit, questo significa che qualcosa dell'ordine di 1 / 1.000 di uno per cento della coppia di ingressi non semplicemente troppo pieno.

Perché non c'è modo di rappresentare tutte le potenze intere in un int in ogni modo:

>>> print 2**-4
0.0625

Che in realtà è una domanda interessante. Un argomento non ho trovato nella discussione è la semplice mancanza di valori di ritorno evidenti per gli argomenti. Contiamo i modi la funzione int pow_int(int, int) hypthetical potrebbe fallire.

  1. Overflow
  2. risultato indefinito pow_int(0,0)
  3. pow_int(2,-1)
  4. Il risultato non può essere rappresentato

La funzione ha almeno 2 modalità di guasto. I numeri interi non può rappresentare questi valori, avrebbero bisogno il comportamento della funzione in questi casi ad essere definito dalla norma -. E programmatori avrebbero bisogno di essere a conoscenza di come esattamente le maniglie funzione questi casi

Nel complesso lasciando la funzione di fuori sembra l'unica opzione ragionevole. Il programmatore può utilizzare la versione in virgola mobile con tutte le segnalazione degli errori invece disponibile.

Breve risposta:

Una specializzazione di pow(x, n) dove n è un numero naturale è spesso utile per prestazioni in tempo . Ma pow() generica della libreria standard funziona ancora abbastanza ( sorprendentemente! ) bene per questo scopo ed è assolutamente fondamentale per comprendere il meno possibile nella libreria standard C in modo che possa essere reso più portabile e facile attuare possibile. D'altra parte, che non si ferma affatto di essere nella libreria standard C ++ o STL, che sono abbastanza sicuro che nessuno sta pensando di usare in una sorta di piattaforma embedded.

Ora, per la risposta a lungo.

pow(x, n) può essere reso molto più veloce in molti casi specializzandosi n ad un numero naturale. Ho dovuto usare la mia implementazione di questa funzione per quasi tutti i programmi che scrivo (ma io scrivere un sacco di programmi matematiche in C). L'operazione specializzata può essere fatto in tempo O(log(n)), ma quando n è piccolo, una versione più lineare, più semplice può essere più veloce. Qui ci sono implementazioni di entrambi:


    // Computes x^n, where n is a natural number.
    double pown(double x, unsigned n)
    {
        double y = 1;
        // n = 2*d + r. x^n = (x^2)^d * x^r.
        unsigned d = n >> 1;
        unsigned r = n & 1;
        double x_2_d = d == 0? 1 : pown(x*x, d);
        double x_r = r == 0? 1 : x;
        return x_2_d*x_r;
    }
    // The linear implementation.
    double pown_l(double x, unsigned n)
    {
        double y = 1;
        for (unsigned i = 0; i < n; i++)
            y *= x;
        return y;
    }

(ho lasciato x e il valore restituito come doppie perché il risultato del pow(double x, unsigned n) si inserisce in un doppio su tutte le volte che sarà pow(double, double).)

(Sì, pown è ricorsiva, ma rompendo lo stack è assolutamente impossibile, poiché la dimensione massima dello stack sarà approssimativamente uguale log_2(n) e n è un numero intero. Se n è un intero a 64 bit, che ti dà una dimensione massima dello stack di circa 64. n hardware ha tali limitazioni di memoria estrema, ad eccezione di alcuni PIC dubbia con pile hardware che vanno solo 3 a 8 chiamate di funzione profondo.)

Per quanto riguarda le prestazioni, sarete sorpresi da ciò che un pow(double, double) varietà giardino è in grado di. Ho provato un centinaio di milioni di iterazioni sul mio 5 anni, IBM Thinkpad con x uguale al numero di iterazioni e n uguale a 10. In questo scenario, pown_l vinto. pow() glibc ha preso 12,0 secondi utente, pown ha 7,4 secondi di utenti, e pown_l ha preso solo 6,5 secondi utente. In modo che non è troppo sorprendente. Siamo stati più o meno aspettavamo questo.

Poi, ho lasciato x costante sia (ho impostato a 2,5), e ho in loop n 0-19 cento milioni di volte. Questa volta, del tutto inaspettatamente, pow glibc vinto, e da una frana! Ci sono voluti solo 2,0 secondi utente. Il mio pown ha 9,6 secondi, e pown_l ha preso 12,2 secondi. Cos'è successo qua? Ho fatto un altro test per scoprirlo.

Ho fatto la stessa cosa come sopra solo con x pari a un milione. Questa volta, pown vinto a 9.6s. pown_l prese 12.2s e pow glibc preso 16.3s. Ora, è chiaro! glibc esegue pow migliore rispetto alla tre quando x è basso, ma peggio quando x è alto. Quando x è alto, esegue pown_l meglio quando n è basso, ed esegue pown meglio quando x è alto.

Così qui sono tre diversi algoritmi, ognuno in grado di svolgere meglio degli altri sotto le giuste circostanze. Quindi, in definitiva, quale utilizzare molto probabilmente dipende da come si sta pensando di usare pow, ma usando la versione corretta è vale la pena, e aventi tutte le versioni è bello. In realtà, si potrebbe anche automatizzare la scelta di un algoritmo con una funzione come questa:

double pown_auto(double x, unsigned n, double x_expected, unsigned n_expected) {
    if (x_expected < x_threshold)
        return pow(x, n);
    if (n_expected < n_threshold)
        return pown_l(x, n);
    return pown(x, n);
}

Finché x_expected e n_expected sono costanti decise in fase di compilazione, insieme con eventuali altri avvertimenti, un compilatore vale ottimizzando suo sale rimuoverà automaticamente l'intera chiamata funzione pown_auto e sostituirla con la scelta appropriata dei trealgoritmi. (Ora, se in realtà si sta andando a tentare di utilizzo questo, probabilmente dovrete giocare con un po ', perché non ho esattamente provo compilazione quello che ho 'd scritto sopra;.))

D'altra parte, glibc pow funziona e glibc è abbastanza grande già. Lo standard C dovrebbe essere portatile, tra cui per varie dispositivi embedded (di fatto incorporato sviluppatori in tutto il mondo concordano sul fatto che glibc è già troppo grande per loro), e non può essere portatili se per ogni semplice funzione matematica ha bisogno di includere ogni algoritmo alternativo che potrebbe essere utile. Quindi, questo è il motivo per cui non è nello standard C.

nota: Nel test delle prestazioni tempo, ho dato le mie funzioni relativamente generosi flag di ottimizzazione (-s -O2) che sono suscettibili di essere paragonabile, se non peggiore di, ciò che è stato probabilmente utilizzato per glibc compilazione sul mio sistema (Archlinux), in modo da i risultati sono probabilmente giusto. Per un test più rigorosi, avrei dovuto compilare glibc me stesso e reeeally non ho voglia di farlo. Ho usato per usare Gentoo, così mi ricordo quanto tempo ci vuole, anche quando il compito è automatizzato . I risultati sono conclusivi (o piuttosto inconcludenti) abbastanza per me. Sei naturalmente invitati a farlo da soli.

Bonus Round: Una specializzazione di pow(x, n) a tutti gli interi è strumentale se è richiesta una potenza esatta intero che accade. Considerare l'allocazione di memoria per un array N-dimensionale con p ^ N elementi. Come p ^ N nemmeno per un comporterà un segfault possibilmente caso si verifichi.

Una ragione di C ++ per non avere sovraccarichi aggiuntivi è quello di essere compatibile con C.

C ++ 98 ha funzioni come double pow(double, int), ma questi sono stati rimossi in C ++ 11 con l'argomento che C99 non includendoli.

http: // www. open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3286.html#550

Ottenere un risultato leggermente più accurato significa anche ottenere un po ' diverso risultato.

Il mondo è in continua evoluzione e così sono i linguaggi di programmazione. La quarta parte del C decimale TR ¹ aggiunge alcune più funzioni a <math.h>. Due famiglie di queste funzioni possono essere di interesse per questa domanda:

  • Le funzioni pown, che richiede un numero decimale e un esponente intmax_t.
  • Le funzioni powr, che prende due galleggianti numeri punti (x e y) e x calcolare al y potenza con il exp(y*log(x)) formula.

Sembra che i ragazzi normali eventualmente ritenuti queste caratteristiche abbastanza utile da integrare nella libreria standard. Tuttavia, il razionale è che queste funzioni sono raccomandati dal ISO / IEC / IEEE 60559: 2011 Standard per i numeri in virgola binarie e decimale fluttuante. Non posso dire con certezza che cosa "standard" è stato seguito al momento della C89, ma le future evoluzioni del <math.h> saranno probabilmente fortemente influenzati dalle future evoluzioni del ISO / IEC / IEEE 60559 di serie .

Si noti che la quarta parte del TR decimale non sarà incluso nel C2x (la prossima importante revisione C), e probabilmente sarà incluso in seguito come una funzione opzionale. Non c'è stato alcun intento che conosco per includere questa parte del TR in una futura revisione C ++.


¹ È possibile trovare della documentazione work-in-progress qui .

Forse perché ALU del processore non attuare tale funzione per gli interi, ma c'è una tale istruzione FPU (come sottolinea Stephen, è in realtà una coppia). Così è stato effettivamente più veloce di fusione per raddoppiare, pow chiamata con i doppi, quindi prova per troppo pieno e indietro cast, oltre a implementare utilizzando aritmetica intera.

(per dirne una, logaritmi ridurre poteri a moltiplicazione, ma logaritmi di numeri interi perdere un sacco di precisione per la maggior parte degli ingressi)

Stefano è giusto che su processori moderni questo non è più vero, ma lo standard C quando le funzioni matematiche sono stati selezionati (C ++ appena usato le funzioni C) è ora quello che, 20 anni?

Un motivo molto semplice:

5^-2 = 1/25

Tutto nella libreria STL è basato sulla più preciso, robusto immaginabile roba. Certo, l'int sarebbe tornare a zero (da 1/25), ma questa sarebbe una risposta imprecisa.

Sono d'accordo, è strano in alcuni casi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top