HOWTO · C++

Berechnen Sie den Winkel zwischen zwei Vektoren in C++

Dieses Tutorial demonstriert die Berechnung eines Winkels zwischen zwei Vektoren in C++.

Auf dieser Seite

Berechnen Sie den Hauptwinkel zwischen zwei gleichdimensionalen Vektoren mit dem Skalarprodukt. Für die Vektoren u und v lautet die Formel theta = acos((u · v) / (|u| * |v|)). Berechnen Sie in C++ das Skalarprodukt und den Betrag jedes Vektors, begrenzen Sie den Kosinuswert wegen möglicher Rundungsfehler bei Gleitkommazahlen auf [-1, 1] und übergeben Sie ihn an std::acos. Das Ergebnis wird in Radiant angegeben; für Grad multiplizieren Sie es mit 180 / pi.

Skalarprodukt und Winkelformel verstehen

Ein Vektor ist eine geordnete Folge von Komponenten, zum Beispiel (x, y) oder (x, y, z). Sein Betrag entspricht seiner Länge und wird als Quadratwurzel aus der Summe der quadrierten Komponenten berechnet. Das Skalarprodukt zweier Vektoren ist die Summe der Produkte ihrer jeweils zugehörigen Komponenten. Zum Beispiel gilt für u · v: u[0] * v[0] + u[1] * v[1] + ....

Der Kosinus des Winkels ergibt sich, indem Sie das Skalarprodukt durch das Produkt der beiden Beträge teilen. Die Umkehrfunktion des Kosinus (acos) liefert den ungerichteten Hauptwinkel von 0 bis 180 Grad (oder von 0 bis pi Radiant). Die Formel funktioniert in 2D, 3D und höheren Dimensionen, sofern beide Vektoren gleich viele Komponenten haben. Eine Orientierung wie im oder gegen den Uhrzeigersinn liefert sie nicht.

Winkel mit einem C++-Array berechnen

Dieses vollständige Beispiel verwendet zwei eingebaute Arrays mit derselben festgelegten Länge. Die Funktion gibt false zurück, wenn der Betrag eines der Vektoren null ist, denn für einen Nullvektor ist kein Winkel definiert. Andernfalls berechnet sie den Kosinus, begrenzt ihn auf den mathematisch gültigen Bereich und gibt das Ergebnis in Grad aus.

Führen Sie dieses Beispiel aus:

#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>

bool calculateAngleDegrees(const double u[], const double v[], int size,
                           double& angleDegrees) {
    double dot = 0.0;
    double squaredU = 0.0;
    double squaredV = 0.0;

    for (int i = 0; i < size; ++i) {
        dot += u[i] * v[i];
        squaredU += u[i] * u[i];
        squaredV += v[i] * v[i];
    }

    if (squaredU == 0.0 || squaredV == 0.0) {
        return false;
    }

    double cosine = dot / std::sqrt(squaredU * squaredV);
    cosine = std::max(-1.0, std::min(1.0, cosine));
    const double pi = std::acos(-1.0);
    angleDegrees = std::acos(cosine) * 180.0 / pi;
    return true;
}

int main() {
    const int size = 3;
    const double u[size] = {1.0, 0.0, 0.0};
    const double v[size] = {0.0, 1.0, 0.0};
    double angleDegrees = 0.0;

    if (calculateAngleDegrees(u, v, size, angleDegrees)) {
        std::cout << std::fixed << std::setprecision(1)
                  << "Angle: " << angleDegrees << " degrees\n";
    } else {
        std::cout << "An angle requires two nonzero vectors.\n";
    }
}

Die beiden Beispielvektoren stehen senkrecht aufeinander; der berechnete Winkel beträgt daher 90 Grad.

Winkel mit std::array berechnen

std::array stellt eine Folge fester Größe dar und speichert die Elementanzahl in ihrem Typ. Diese Variante verwendet C++11 oder höher und leitet die Anzahl aus dem Array-Typ ab. Dadurch haben beide Eingaben gleich viele Komponenten. Die Berechnung und die Behandlung des Nullvektors entsprechen der Variante mit eingebauten Arrays.

#include <algorithm>
#include <array>
#include <cmath>
#include <iomanip>
#include <iostream>

bool calculateAngleDegrees(const std::array<double, 3>& u,
                           const std::array<double, 3>& v,
                           double& angleDegrees) {
    double dot = 0.0;
    double squaredU = 0.0;
    double squaredV = 0.0;

    for (std::size_t i = 0; i < u.size(); ++i) {
        dot += u[i] * v[i];
        squaredU += u[i] * u[i];
        squaredV += v[i] * v[i];
    }

    if (squaredU == 0.0 || squaredV == 0.0) {
        return false;
    }

    double cosine = dot / std::sqrt(squaredU * squaredV);
    cosine = std::max(-1.0, std::min(1.0, cosine));
    const double pi = std::acos(-1.0);
    angleDegrees = std::acos(cosine) * 180.0 / pi;
    return true;
}

int main() {
    const std::array<double, 3> u = {1.0, 0.0, 0.0};
    const std::array<double, 3> v = {0.0, 1.0, 0.0};
    double angleDegrees = 0.0;

    if (calculateAngleDegrees(u, v, angleDegrees)) {
        std::cout << std::fixed << std::setprecision(1)
                  << "Angle: " << angleDegrees << " degrees\n";
    } else {
        std::cout << "An angle requires two nonzero vectors.\n";
    }
}

Verwenden Sie std::array<double, N>, wenn die Dimension feststeht und bereits zur Compile-Zeit bekannt ist. Die Berechnung wird dadurch nicht grundsätzlich effizienter. Der Hauptvorteil hier ist, dass die Größe Teil des Typs ist und mit .size() abgerufen werden kann.

Nullvektoren, Dimensionen und gerichtete Winkel behandeln

Ein Winkel ist nicht definiert, wenn einer der Vektoren den Betrag null hat. Prüfen Sie dies vor der Division durch das Produkt der Beträge, wie es die Beispiele tun. Geben Sie bei eingebauten Arrays die tatsächlich gemeinsame Länge an und stellen Sie sicher, dass beide Arrays mindestens so viele Elemente enthalten. Bei std::array erzwingt dieselbe Größe N in beiden Typen gleiche Dimensionen.

Die Formel mit dem Skalarprodukt liefert einen ungerichteten Hauptwinkel. In 2D lässt sich ein gerichteter Winkel mit atan2(cross, dot) berechnen, wobei das skalare Kreuzprodukt u.x * v.y - u.y * v.x ist. In 3D misst die Formel für den gerichteten Winkel atan2(n · (u × v), u · v) den Winkel in der Ebene senkrecht zu einer gewählten Einheitsnormalen n, sofern beide Vektoren in dieser Ebene liegen. Liegt einer der Vektoren außerhalb der gewünschten Ebene, projizieren Sie zuerst beide Vektoren auf diese Ebene und wenden Sie die Formel auf die Projektionen an. Das Ergebnis ist dann der Winkel zwischen den projizierten Vektoren. Die Normale legt die positive Richtung fest; sie wird nicht zur Formel mit dem Skalarprodukt addiert. Diese Verfahren liefern Radiant, die Sie mit demselben Faktor wie oben in Grad umrechnen können.

Häufig gestellte Fragen

Was sagt mir das Skalarprodukt? Es fasst die jeweils zugehörigen Komponenten zu einem Skalar zusammen. Im Verhältnis zu den Beträgen bestimmt sein Wert den Kosinus des Winkels: Bei einem spitzen Hauptwinkel ist er positiv, bei senkrechten Vektoren null und bei einem stumpfen Hauptwinkel negativ.

Gibt std::acos Grad zurück? Nein. Das Ergebnis wird in Radiant angegeben. Multiplizieren Sie Radiant mit 180 / pi, um Grad zu erhalten. Sie können auch Radiant beibehalten, wenn der übrige Programmcode diese Einheit verwendet.

Dürfen die Vektoren unterschiedliche Dimensionen haben? Nein. Ein Skalarprodukt ordnet jeweils zusammengehörige Komponenten einander zu, daher müssen beide Vektoren dieselbe Dimension haben. Verwenden Sie für eingebaute Arrays eine gemeinsame, korrekte Länge. Bei std::array ist die Größe im Typ erkennbar.

Kann ich die Formel in 3D verwenden? Ja. Die Formel mit Skalarprodukt und Betrag gilt für alle gleichdimensionalen Vektoren, auch für 3D-Vektoren. Verwenden Sie nur dann eine Berechnung des gerichteten Winkels mit einer Bezugsnormalen, wenn die Orientierung relevant ist.

Sollte ich eine Vektormathematikbibliothek verwenden? Für eine kleine Berechnung reichen die Beispiele oben aus. Eine Bibliothek wie Eigen oder GLM kann hilfreich sein, wenn ein Projekt bereits deren Vektor- und Matrixtypen verwendet oder weitere Operationen benötigt.