Arduino - ungerade Exponenten Verhalten mit pow(x,y) - Funktion
Ich habe in den letzten paar Stunden debugging dieses problem, und schließlich löste es selbst. Dachte, ich würde es hier posten, um zu verhindern, dass andere mit dem gleichen problem lächerlich.
Ich würde auf jeden Fall offen sein, um eine tiefere Erklärung, wie warum meine Antwort ist eine Erklärung.
Arbeite ich mit der pow(x,y) - Funktion zum zurückgeben Exponenten. Ich war zu bemerken, sehr komisches Verhalten mit dem Exponenten, und ich kann nicht ganz verstehen, warum. Hier ist mein code:
for (int n=0;n<5;n++)
{
int x = pow(2,n);
Serial.print(n);
Serial.print(" ");
Serial.println(x);
}
Und hier ist meine Ausgabe:
0 1
1 2
2 3
3 7
4 15
Also diese zahlen sind offensichtlich nicht richtig. Komisch ist, wenn ich den gleichen code in einem C++ - Programm in Xcode (mit cout-Anweisungen anstelle von Serieller Ausgang) bekomme ich die folgende (was ich erwarten würde):
0 1
1 2
2 4
3 8
4 16
Warum in der Welt würde diese Rückkehr voraussichtlich meine Werte in Xcode, aber nicht auf dem arduino? Warum funktioniert der arduino zurück pow(2,n) = 2^n-1
für jeden Wert n größer als 1?
- Meinst du die Verwendung
1<<n
? - nicht sicher, was du damit meinst. Habe ich meine große Werte von n? Nein.
- Ich meine, dass
(int)pow(2,n)
ist etwas, das sollte nicht geschrieben werden.pow
ist für floating point. C++ hatoperator<<
, wo1<<n
gibt die N-te Potenz von 2 ist.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Da der AVR nicht über eine FPU,
pow()
im avr-libc implementiert ist, über Aufrufelog()
undexp()
. Wieder, aufgrund der fehlenden FPU, avr-libc verwendet Annäherungen für diese beiden Funktionen. Dies führt zu Werten, dass etwas von dem wahren Wert, der beim cast auf einen integer-verlieren können die wenigsten signifikanten stellen.Dies nicht passiert, x86-basierte Systeme, da diese die hardware-FPUs, die fähig ist, die wahre Integrale Wert für nicht-negative Potenzen von positiven ganzen zahlen.
Mein Vorschlag ist, dass, wenn alles, was Sie brauchen, ist nicht-negativ integral Potenzen von ganzen zahlen, dann sollten Sie führen eine Reihe von bitweise Verschiebungen und fügt hinzu, anstatt Sie zu verknüpfen, in der nicht-trivialen, nicht-exakte
libm
.Ah, du Jugendlicher Narr, Ryan! Haben Sie kein Verständnis, Daten-Typen!?
Die Arduino-pow () - Referenz ausdrücklich, dass diese Werte übergeben werden müssen, die als Gleitkommazahlen, und kehrte als verdoppelt! Verwenden wir also einige Gehirnzellen und zumindest versuchen, Rückgabe einem Doppel!
Hier ist etwas code zu markieren, die Verrücktheit, dass ist Los:
Und hier ist Ihre Ausgabe:
Egal, das wird Ihr problem lösen. Gießen Sie die Zahl als int-zeigt, es wird abgerundet.
Nun, warum geschieht dies? Nicht genau sicher.
nearbyint
,rint
,round
usw vor der Besetzung.pow
- Funktion wurde implementiert. Es erhält 2 double-Werte, also kann es sich nicht richtig für ganzzahlige Exponenten aufgrund der Art, wie es berechnet pow(x, y) = exp(x*ln(y)). Und so macht der 2 ist einfach "falsch".1 << y
ist Hunderte Male schneller alspow(2, y)
auf einem 8-bit-mikrocontrollerNur als Ergänzung zu Ignacio Vazquez-Abrams' Antwort (was ist das
richtige Antwort), ich schrieb das folgende Programm zum testen für die Richtigkeit der
pow(2, i)
für positive ganzzahlige Werte voni
:Auf meinem PC (gcc 5.4.0 /Ubuntu 16.04), dieses Programm meldet keine Fehler.
Läuft das gleiche Programm (mit der richtigen stdio setup) auf einem Arduino Uno
(avr-gcc-4.9.2 /avr-libc 1.8.0) bekomme ich Fehler, so groß wie
90 ULPs! Hier ist die Ausgabe aus der Uno:
Ein paar Punkte erwähnenswert:
FLT_MIN
undFLT_MAX
ist genaudarstellbar als
float
korrekt gerundet, was bedeutet, dass computing 2i, die von
iterative Multiplikation mit 2 ist garantiert, um die genaue Ergebnis
pow
werden korrekt gerundet.Alte Frage, aber für die Zukunft askers:
Den Grund 16.0 gerundet auf 15, ist, dass die Konvertierung von float zu int ist immer durch abschneiden der Nachkommastellen. So 15.99999 noch wird 15 und nicht 16.
Da die floating-point-Werte nicht exakt enthalten, 16.0, Sie halten so etwas wie 15.99963513 (random Beispiel), die wird 15 nach der Umwandlung in eine ganze Zahl.