2012-03-28 9 views
40

Berechnung PI-Wert ist eine der komplexen Problem und wikipedia spricht über die approximations dafür getan und sagt, es ist schwierig, PI genau zu berechnen.Mathe-Konstante PI-Wert in C

Wie berechnet C PI? Berechnet es es jedes Mal oder verwendet es einen weniger genauen festen Wert?

+14

[Die Verwendung von pi, berechnet auf nur 39 Dezimalstellen, würde es erlauben, den Umfang des gesamten Universums mit der Genauigkeit von weniger als dem Durchmesser eines Wasserstoffatoms zu berechnen] (http://danielmiessler.com/blog/the- verrückteste Ding-Youll-überhaupt-lernen-über-pi). 16 Dezimalstellen (ungefähr, was man mit einem "Doppel" erhält) sollten ausreichen, um den Durchmesser des Sonnensystems mit dem Fehler kleiner als eine Haarbreite zu berechnen. – pmg

Antwort

49

In C Pi wird in math.h definiert: #define M_PI 3.14159265358979323846

+1

Und dieser Wert ist die genaueste Darstellung, die für einen Wert mit doppelter Genauigkeit verfügbar ist. –

+23

Nicht ganz - tatsächlich kann eine konforme C-Implementierung * PI nicht in '' definieren. POSIX spezifiziert 'M_PI', aber wiederum kann eine konforme C-Implementierung es nicht definieren. (POSIX erlegt einige Anforderungen auf, die dem C-Standard widersprechen.) Aber Sie können es in Ihrem eigenen Programm so definieren. –

+0

Wenn Sie Pech mit Ihrem mathematischen Header haben, verwenden Sie #define von GP. –

4

trotzdem haben Sie keine unbegrenzte Genauigkeit so C eine Konstante in dieser Art und Weise definieren:

#define PI 3.14159265358979323846 

Import math.h diesen

zu verwenden
+0

Das bedeutet, dass ich eine höhere Genauigkeit für PI erreichen kann als durch C definiert? Wenn ich double x = 1345 * PI verwende; Aber es ist begrenzt durch die Genauigkeit der Doppel-Variable im Programm nicht verwendet wird ??? Das bedeutet, neu definierte genaue Wert ist nutzlos ?? – user1298016

+0

teilweise ja: D ... "Doppel x = 1345 * PI" durch diese Arbeit verlieren Sie etwas Genauigkeit, weil PI so genau ist wie es kann. Wenn Sie mehr Genauigkeit wünschen, sollten Sie Ihre eigene Struktur implementieren und das Ergebnis in einem Array speichern (wie BigInteger in Java). OK? –

+1

Eigentlich 'M_PI' ist nicht in C. Es ist Teil der XSI-Erweiterung Option in POSIX. –

14

Das nächste, was C tut, um "π" auf eine Weise zu berechnen, die für Anwendungen direkt sichtbar ist, ist acos(-1) oder ähnlich. Dies geschieht fast immer mit polynomiellen/rationalen Approximationen für die zu berechnende Funktion (entweder in C oder durch den FPU-Mikrocode). Ein interessantes Problem ist jedoch, dass die Berechnung der trigonometrischen Funktionen (sin, cos und tan) eine Reduktion ihres Arguments modulo 2 & pgr; erfordert. Da 2π kein diadikal rational (und nicht einmal rational) ist, kann es nicht in irgendeinem Fließkommatyp dargestellt werden, und daher führt die Verwendung einer Näherung des Werts zu einer katastrophalen Fehlerakkumulation für große Argumente (zB wenn x1e12 ist und 2*M_PI unterscheidet sich von 2π durch ε unterscheidet sich dann fmod(x,2*M_PI) vom richtigen Wert von 2π um bis zu 1E12 * ε/π mal den richtigen Wert von x mod 2π. das heißt, es.

A die korrekte Umsetzung von C völlig sinnlos ist Die Standard-Mathebibliothek hat einfach eine gigantische sehr hochpräzise Darstellung von π hart codiert in ihrer Quelle, um mit dem Problem der korrekten Argumentreduktion umzugehen (und verwendet einige ausgefallene Tricks, um es nicht ganz so gigantisch zu machen) die meisten/alle C-Versionen der sin/cos/tan Funktionen funktionieren. Bestimmte Implementierungen (wie glibc) sind jedoch dafür bekannt, Assembly-Implementierungen auf einigen CPUs (wie x86) zu verwenden und führen keine korrekte Argumentreduktion durch, was zu völlig unsinnigen Ausgaben führt. (Übrigens ist der falsche asm läuft in der Regel über die gleiche Geschwindigkeit wie die richtige C-Code für kleine Argumente.)

+0

Haben Sie Referenzen für Ihren letzten Absatz? Aus meiner Intuition, wenn Sie 'sin (1e16 * M_PI)' berechnen, ist die Mantisse des Arguments vollständig links vom Dezimalpunkt und es gibt keine Nachkommastellen. Es sollte also nicht darauf ankommen, wie Sie Ihre Argumentation reduzieren, denn Sie werden niemals Informationen wiederherstellen, die nicht an erster Stelle standen (nämlich der Bruchteil der Mantisse). – Fritz

+0

@Fritz: Der Fraktionsteil des Eingabearguments ist Null - das Argument ist eine Ganzzahl (eine sehr große). Die Argumentreduktion führt nicht zur "Wiederherstellung von Informationen".Bei einem großen 'x' erhalten Sie einen Wert' y', für den (mathematisch) '| sin (x) -sin (y) |

+0

Nach der Argumentreduktion eines großen Arguments ist der Bruchteil ganz sicher nicht Null, weil Pi nicht rational ist (obwohl technisch nach einer Rundung Null sein kann, für sehr seltene Fälle). –

13

definieren sich vor:

#define M_PI acos(-1.0) 

Es sollte Ihnen genau PI-Nummer geben, dass mathematische Funktionen mit denen sie arbeiten. Also, wenn sie ändern PI-Wert arbeiten sie mit in Tangens oder Cosinus oder Sinus, dann sollte Ihr Programm immer auf dem neuesten Stand sein;)

+0

Dies würde recht kostspielige Funktion (acos) jedes Mal, wenn die Konstante verwendet wird. Kaum ein effektiver und rationaler Ansatz. –

+0

Ich habe diese Lösung für die beste Lösung mit #define gemacht. Wenn jemand ein Problem damit hat, dann können sie eine globale Variable definieren: double M_PI = acos (-1.0); –

+0

@ JaromírAdamec Eigentlich sollte jeder gute Compiler diesen Ausdruck in eine Konstante optimieren, die eine reine Funktion aus der Standardbibliothek ist, die mit konstanten Argumenten aufgerufen wird; es sei denn, Sie kompilieren mit Optimierungen aus, das ist. – UnrealEagle

0

Abhängig von der Bibliothek verwenden Sie die Standard-GNU C vordefinierte mathematische Konstanten sind hier ... https://www.gnu.org/software/libc/manual/html_node/Mathematical-Constants.html

Sie haben sie schon, also warum sie neu definieren? Ihre Desktop-Rechner haben sie wahrscheinlich und sind noch genauer, so dass Sie nur sicher sein können, dass Sie nicht mit bereits definierten kollidieren, um Kompilierungswarnungen zu speichern, da sie dazu neigen, Standardwerte für solche Dinge zu erhalten. Genießen Sie!

0

Da dieser C und nicht C++ wir dies tun könnte:

etwas.h

static const float PI; 

some.c

#include "some.h" 

const float = 4.0f * atan(1.0f); // tan(pi/4) = 1 or acos(-1) 

IMHO bin ich nicht 100% sicher, aber ich denke atan() ist billiger als acos().

Wenn Sie höhere Präzision Änderung des float zu double oder long double wenn die long double von Ihrem Compiler unterstützt wird.