Template Funktion Array min/max mit signed und unsigned Datentypen

Arduino IDE: 1.8.10
Board: Esp8266 Nodemcu
Esp Core Version: 2.6.2

Mit Vorzeichenlosen Werten im Array ist es einfach.

template <typename T, size_t size> T maxRead(T (&value)[size]) {
  T maxValue {0};
  for (auto &v : value) maxValue = max(maxValue, v);                   // max Wert aus Array ermitteln
  return maxValue;
}

int8_t byteBuffer[] {23, 36, 45, 113, 81, 111};
float floatBuffer[] {6.5, 8.9, 14.2, 5.6, 10.4};
int32_t intBuffer[] {-44823, -41136, -42545, -43613, -49981, -47711};
uint32_t uintBuffer[] {823, 436, 545, 613, 981, 711};

void setup() {
  Serial.begin(115200); delay(100);
  Serial.print("\n\nbyte\t"); Serial.println(maxRead(byteBuffer));
  Serial.print("float\t"); Serial.println(maxRead(floatBuffer));
  Serial.print("int\t"); Serial.println(maxRead(intBuffer));        // geht natürlich nicht
  Serial.print("uint\t"); Serial.println(maxRead(uintBuffer));
}

void loop() {}

Man könnte der Funktion den niedrigsten Wert vom Datentyp mitgeben. Sodas es auch mit negativen Werten funktioniert.
Schön geht anders!

template <typename T, size_t size> T maxRead(T (&value)[size], T maxValue = 0) {
  for (auto &v : value) maxValue = max(maxValue, v);                              // max Wert aus Array ermitteln
  return maxValue;
}

int8_t byteBuffer[] {23, 36, 45, 13, 81, 111};
float floatBuffer[] { -6.5, -8.9, -14.2, -5.6, -10.4};
int32_t intBuffer[] { -44823, -41136, -42545, -43613, -49981, -47711};
uint32_t uintBuffer[] {823, 436, 545, 613, 981, 711};

void setup() {
  Serial.begin(115200);
  delay(100);
  Serial.print("\n\nbyte\t"); Serial.println(maxRead(byteBuffer));
  Serial.print("float\t"); Serial.println(maxRead(floatBuffer, -999.9F));    // nicht schön
  Serial.print("int\t"); Serial.println(maxRead(intBuffer, -2147483647));    // nicht schön
  Serial.print("uint\t"); Serial.println(maxRead(uintBuffer));
}

void loop() {}

Mein Favorit momentan!
"maxValue" zunächst auf den niedrigsten Wert im Array setzen um anschließend den Max Wert zu ermitteln.

template <typename T, size_t size> T maxRead(T (&value)[size]) {
  T maxValue {0};
  for (auto &v : value) maxValue = min(maxValue, v);          // zuerst min Wert ermitteln
  for (auto &v : value) maxValue = max(maxValue, v);          // max Wert aus Array ermitteln
  return maxValue;
}

int8_t byteBuffer[] {23, 36, 45, 113, 81, 111};
float floatBuffer[] { -6.5, -8.9, -14.2, -5.6, -10.4};
int32_t intBuffer[] { -44823, -41136, -42545, -43613, -49981, -47711};
uint32_t uintBuffer[] {823, 436, 545, 613, 981, 711};

void setup() {
  Serial.begin(115200);
  delay(100);
  Serial.print("\n\nbyte\t"); Serial.println(maxRead(byteBuffer));
  Serial.print("float\t"); Serial.println(maxRead(floatBuffer));
  Serial.print("int\t"); Serial.println(maxRead(intBuffer));
  Serial.print("uint\t"); Serial.println(maxRead(uintBuffer));
}

void loop() {}

/*
Äquivalent Dazu den Min Wert ermitteln indem "minValue" zunächst auf den max Wert im Array gesetzt wird.
template <typename T, size_t size> T minRead(T (&value)[size]) {
  T minValue {0};
  for (auto &v : value) minValue = max(minValue, v);
  for (auto &v : value) minValue = min(minValue, v);
  return minValue;
}
*/

Gibt es noch bessere Möglichkeiten als zweimal das Array zu durchlaufen?

Danke und Gruß Fips

maxValue auf den ersten Wert des Arrays setzen?

Gruß Tommy

Tommy56:
maxValue auf den ersten Wert des Arrays setzen?

Das scheint mir der beste Weg zu sein!
template <typename T, size_t size> T maxRead(T (&value)[size]) {
  T maxValue {value[0]};
  for (auto &v : value) maxValue = max(maxValue, v);           // max Wert aus Array ermitteln
  return maxValue;
}
template <typename T, size_t size> T minRead(T (&value)[size]) {
  T minValue {value[0]};
  for (auto &v : value) minValue = min(minValue, v);           // min Wert aus Array ermitteln
  return minValue;
}

int8_t byteBuffer[] {123, 36, 45, 113, 81, 111};
float floatBuffer[] {-1.5, 8.9, 14.2, -5.6, 10.4};
int32_t intBuffer[] {-44823, -41136, -42545, -43613, -49981, -47711};
uint32_t uintBuffer[] {823, 436, 545, 613, 981, 711};

void setup() {
  Serial.begin(115200); delay(100);
  Serial.print("\n\nmax byte\t"); Serial.println(maxRead(byteBuffer));
  Serial.print("max float\t"); Serial.println(maxRead(floatBuffer));
  Serial.print("max int\t"); Serial.println(maxRead(intBuffer));
  Serial.print("max uint\t"); Serial.println(maxRead(uintBuffer));
  
  Serial.print("\n\nmin byte\t"); Serial.println(minRead(byteBuffer));
  Serial.print("min float\t"); Serial.println(minRead(floatBuffer));
  Serial.print("min int\t"); Serial.println(minRead(intBuffer));
  Serial.print("min uint\t"); Serial.println(minRead(uintBuffer));
}

void loop() {}

Ausgabe:

max byte 123
max float 14.20
max int -41136
max uint 981

min byte 36
min float -5.60
min int -49981
min uint 436

Na gucke, so einfach ist es.

Mann, Mann, Mann!! Mein Google ist wohl doch defekt!

Danke und Gruß Fips

Das war nicht Google, das war ein Erfahrungswert. Da bin ich auch vor Jahren mal drüber gestolpert.

Man darf aber nicht denken, dass man alle Fehler schon gemacht hat. Es sind noch genug übrig :wink:

Gruß Tommy

Tommy56:
Da bin ich auch vor Jahren mal drüber gestolpert.

Gut das dir ein stolpern so lange im Gedächtnis bleibt! :smiley:

Gruß Fips

Ich habe das mit Interesse verfolgt.
(und mir natürlich meine Gedanken dazu gemacht)

Vorweg:
Ich finde deine Template Idee gut.
Sie zielt in die richtige Richtung.
Im Detail könnte man evtl. noch was verbessern.

Denke A:
So weit ich mich erinnere arbeitest du viel mit 32 Bit Rechnern, ESP. Da ist die STD lib im Paket dabei.
max_element
Welche mit allen(?) Containern umgehen kann, nicht nur mit Arrays.
Auch kann es alles vergleichen, für das es einen (überschriebenen) Vergleichsoperator gibt.

Denke B:
Leider gibts die STD Lib nicht für AVR

Denke C:
Wenns mein Job wäre, würde ich eine, möglichst weit zum Standard kompatible, "Funktion" für die AVR entwerfen.

Hier ein Programm mit der STD Lib Funktion, getestet mit ESP, ARM und RISC-V:

#include <Streaming.h>
#include <bits/stdc++.h> 

//int arr[]  { 1, 45, 54, 71, 76, 12 , -44823, -41136, -42545, -43613, -49981, -47711}; 
float arr[]  { 1, 45, 54, 71, 76, 12 , -44823, -41136, -42545, -43613, -49981, -47711}; 

int n = sizeof(arr) / sizeof(arr[0]); 

void setup() 
{
  Serial.begin(9600);
  Serial << "Start: "<< __FILE__ << endl;

}

void loop() 
{
  Serial << "Max Element = " << *std::max_element(arr, arr + n)<< endl; 
  Serial << "Min Element = " << *std::min_element(arr, arr + n)<< endl; 
  delay(1000);
}

combie:
Vorweg:
Ich finde deine Template Idee gut.
Sie zielt in die richtige Richtung.

Ich als Laie, muß das jetzt erstmal sacken lassen!

Im Detail könnte man evtl. noch was verbessern.

Denke A:
So weit ich mich erinnere arbeitest du viel mit 32 Bit Rechnern, ESP. Da ist die STD lib im Paket dabei.
max_element
Welche mit allen(?) Containern umgehen kann, nicht nur mit Arrays.

Ja, ausschließlich Esp... Den Uno hatte ich nach drei Monaten bei Seite gelegt.

"STD lib, max_element, min_element" Das kenn ich noch gar nicht!

Schau ich mir heute noch an.

Danke combie

Gruß Fips

"STD lib,......" Das kenn ich noch gar nicht!

Oh, ja, fein!
:grin: Dann hast du jetzt wohl einen frischen neuen mittleren Berg vor Augen. :grin:

combie:
Oh, ja, fein!
:grin: Dann hast du jetzt wohl einen frischen neuen mittleren Berg vor Augen. :grin:

Berg ist passend! ;D

Ich weiß immer gar nicht wo ich anfangen soll. Mit Klassen habe ich auch noch nichts gemacht und der dicke Breymann liegt hier auch noch, fast ungelesen, seit Weihnachten rum.

Hatte zuletzt auch wenig Zeit für die Esp.
Ich hatte mir vor einem Jahr fest vorgenommen mich nicht mehr von WordPress bevormunden zu lassen. Also mal bei PHP und MySQL reingeschaut und ein eigenes Buchungssystem mit Frontend, Backend, Mailversand usw. gebastelt.
Bis heute schon 85 Anmeldungen, für unser Skatturnier, obwohl die Werbung in der Regionalen Presse noch aussteht.
Ist natürlich von Vorteil, wenn man die Grundlagen einer Programmiersprache kennt und weiß womit man die Suchmaschine füttern kann.

Ich schweif wohl etwas ab!

Gruß Fips

Zurück zum Thema:

Sketch mit der bits/stdc++.h

#include <bits/stdc++.h>

template <typename T, size_t size> T maxRead(T (&value)[size]) {
  return *std::max_element(value, value + size);
}
template <typename T, size_t size> T minRead(T (&value)[size]) {
  return *std::min_element(value, value + size);
}

int8_t byteBuffer[] {23, 36, 45, 113, 47, 21, 81, 111};
float floatBuffer[] {-6.5, -8.9, 1.5, -14.2, -5.6, -10.4};
int32_t intBuffer[] {-44823, -41136, -42545, -43613, 49981, -47711};
uint32_t uintBuffer[] {2282300, 1343600, 3554500, 2161300, 3498100, 1771100};

void setup() {
  Serial.begin(115200);
  delay(100);
  Serial.print("\n\nmax byte\t"); Serial.println(maxRead(byteBuffer));
  Serial.print("min byte\t"); Serial.println(minRead(byteBuffer));
  Serial.print("max float\t"); Serial.println(maxRead(floatBuffer));
  Serial.print("min float\t"); Serial.println(minRead(floatBuffer));
  Serial.print("max int\t"); Serial.println(maxRead(intBuffer));
  Serial.print("min int\t"); Serial.println(minRead(intBuffer));
  Serial.print("max uint\t"); Serial.println(maxRead(uintBuffer));
  Serial.print("min uint\t"); Serial.println(minRead(uintBuffer));
  Serial.print("uptime: "); Serial.println(millis());
}

void loop() {}

Der Sketch verwendet 371576 Bytes (35%) des Programmspeicherplatzes. Das Maximum sind 1044464 Bytes.
Globale Variablen verwenden 34824 Bytes (42%) des dynamischen Speichers, 47096 Bytes für lokale Variablen verbleiben. Das Maximum sind 81920 Bytes.

max byte 113
min byte 21
max float 1.50
min float -14.20
max int 49981
min int -47711
max uint 3554500
min uint 1343600
uptime: 175

Sketch ohne bits/stdc++.h

template <typename T, size_t size> T maxRead(T (&value)[size]) {
  T maxValue {value[0]};
  for (auto &v : value) maxValue = max(maxValue, v);           // max Wert aus Array ermitteln
  return maxValue;
}
template <typename T, size_t size> T minRead(T (&value)[size]) {
  T minValue {value[0]};
  for (auto &v : value) minValue = min(minValue, v);           // min Wert aus Array ermitteln
  return minValue;
}

int8_t byteBuffer[] {23, 36, 45, 113, 47, 21, 81, 111};
float floatBuffer[] { -6.5, -8.9, 1.5, -14.2, -5.6, -10.4};
int32_t intBuffer[] { -44823, -41136, -42545, -43613, 49981, -47711};
uint32_t uintBuffer[] {2282300, 1343600, 3554500, 2161300, 3498100, 1771100};

void setup() {
  Serial.begin(115200);
  delay(100);
  Serial.print("\n\nmax byte\t"); Serial.println(maxRead(byteBuffer));
  Serial.print("min byte\t"); Serial.println(minRead(byteBuffer));
  Serial.print("max float\t"); Serial.println(maxRead(floatBuffer));
  Serial.print("min float\t"); Serial.println(minRead(floatBuffer));
  Serial.print("max int\t"); Serial.println(maxRead(intBuffer));
  Serial.print("min int\t"); Serial.println(minRead(intBuffer));
  Serial.print("max uint\t"); Serial.println(maxRead(uintBuffer));
  Serial.print("min uint\t"); Serial.println(minRead(uintBuffer));
  Serial.print("uptime: "); Serial.println(millis());
}

void loop() {}

Der Sketch verwendet 261984 Bytes (25%) des Programmspeicherplatzes. Das Maximum sind 1044464 Bytes.
Globale Variablen verwenden 27056 Bytes (33%) des dynamischen Speichers, 54864 Bytes für lokale Variablen verbleiben. Das Maximum sind 81920 Bytes.

max byte 113
min byte 21
max float 1.50
min float -14.20
max int 49981
min int -47711
max uint 3554500
min uint 1343600
uptime: 171

Der Unterschied beim Speicherverbrauch ist schon enorm.

Gruß Fips

Das ist er.
Woran das liegt? KA!

Hi

Mir kamen die Zahlen doch recht fett vor - also den Kram gerade Mal für nen Nano/Uno kompiliert:
Der Sketch verwendet 4066 Bytes (13%) des Programmspeicherplatzes. Das Maximum sind 30720 Bytes.
Globale Variablen verwenden 370 Bytes (18%) des dynamischen Speichers, 1678 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.

Was macht der ESP, wenn Der mit dem Sketch, Der Den AVR kaum belasten würde, fertig ist?
Bei den Zahlen müsste die halbe Steuersoftware eines mittelprächtigen Kernkraftwerk dort noch mit drin sein :confused:

MfG

void setup() {
}
void loop() {
}

Der Sketch verwendet 255044 Bytes (24%) des Programmspeicherplatzes. Das Maximum sind 1044464 Bytes.
Globale Variablen verwenden 26772 Bytes (32%) des dynamischen Speichers, 55148 Bytes für lokale Variablen verbleiben. Das Maximum sind 81920 Bytes.

Der Esp ist mit so einen Sketch auch im Wlan, wenn er es vorher war.

Gruß Fips

Ja, da wird ein relativ großes Grundsystem geladen, im Gegensatz zum AVR.

Gruß Tommy

Nun fehlt mir noch der Durchschnitt aller Elemente im Array.

Dazu findet man meistens den Ansatz, Summe aller Elemente durch Anzahl eben dieser!

Ist die Summe aber Größer als der Datentyp wird es Mist.

template <typename T, size_t size> T checkRead(T (&fifo)[size]) {
  T average {0};
  for (auto& value : fifo) average += value;
  return average / size;
}

void setup() {
  Serial.begin(115200); delay(100);

  uint8_t byteBuffer1[] {5, 3, 2, 4, 5, 1, 3, 2, 4, 5, 1, 2, 3, 4, 2, 1, 6, 3, 2, 4, 1, 5, 3, 2, 4, 2, 5, 3, 2, 4, 3, 5, 1, 1, 2};
  Serial.print("\n\nbyte: "); Serial.print(checkRead(byteBuffer1)); Serial.println(" Erwartet: 3");
  
  // Summe passt nicht in den Datentyp
  int8_t byteBuffer2[] {67, 107, 115, 123, 103, 116, 112, 105, 93, 4, 91, 99, 114, 111, 119, 118, 108, 89, 121, 77, 79, 97, 87, 59, 127, 126, 125, 124, 123};
  Serial.print("byte: "); Serial.print(checkRead(byteBuffer2));  Serial.println(" Erwartet: 101");

  float floatBuffer[] { -1.5, 8.9, 14.2, -5.6, 10.4, 2.3, 87.9, 3.3, 4.7};
  Serial.print("float: "); Serial.print(checkRead(floatBuffer)); Serial.println(" Erwartet: 13.84");
  
  // Summe passt nicht in den Datentyp
  uint32_t uintBuffer1[] {2134567890, 1234567890, 987654321, 613, 981};
  Serial.print("uint32: "); Serial.print(checkRead(uintBuffer1)); Serial.println(" Erwartet: 871358339");

  uint64_t uintBuffer2[] {2134567890, 1234567890, 987654321, 613, 981};
  Serial.print("uint64: "); Serial.print((uint32_t)checkRead(uintBuffer2)); Serial.println(" Erwartet: 871358339");
}

void loop() {}

Ausgabe:

byte: 3 Erwartet: 3
byte: 4 Erwartet: 101
float: 13.84 Erwartet: 13.84
uint32: 12364879 Erwartet: 871358339
uint64: 871358339 Erwartet: 871358339

Mein momentaner Favorit, jedes Element erstmal durch die Anzahl derer Teilen.

template <typename T, size_t size> T averageRead(T (&fifo)[size]) {
  double average {0};
  for (auto& value : fifo) average += (double)value / size;
  return (T)average;
}

void setup() {
  Serial.begin(115200); delay(100);

  uint8_t byteBuffer1[] {5, 3, 2, 4, 5, 1, 3, 2, 4, 5, 1, 2, 3, 4, 2, 1, 6, 3, 2, 4, 1, 5, 3, 2, 4, 2, 5, 3, 2, 4, 3, 5, 1, 1, 2};
  Serial.print("\n\nbyte: "); Serial.print(averageRead(byteBuffer1)); Serial.println(" Erwartet: 3");
  
  int8_t byteBuffer2[] {67, 107, 115, 123, 103, 116, 112, 105, 93, 4, 91, 99, 114, 111, 119, 118, 108, 89, 121, 77, 79, 97, 87, 59, 127, 126, 125, 124, 123};
  Serial.print("byte: "); Serial.print(averageRead(byteBuffer2));  Serial.println(" Erwartet: 101");

  float floatBuffer[] { -1.5, 8.9, 14.2, -5.6, 10.4, 2.3, 87.9, 3.3, 4.7};
  Serial.print("float: "); Serial.print(averageRead(floatBuffer)); Serial.println(" Erwartet: 13.84");

  uint32_t uintBuffer1[] {2134567890, 1234567890, 987654321, 613, 981};
  Serial.print("uint32: "); Serial.print(averageRead(uintBuffer1)); Serial.println(" Erwartet: 871358339");

  uint64_t uintBuffer2[] {2134567890, 1234567890, 987654321, 613, 981};
  Serial.print("uint64: "); Serial.print((uint32_t)averageRead(uintBuffer2)); Serial.println(" Erwartet: 871358339");
}

void loop() {}

Ausgabe:

byte: 2 Erwartet: 3
byte: 101 Erwartet: 101
float: 13.84 Erwartet: 13.84
uint32: 871358339 Erwartet: 871358339
uint64: 871358339 Erwartet: 871358339

Das kommt der Erwartung schon ziemlich nahe.

Hat jemand eine Idee, das ganze zu verbessern?

Gruß Fips

(a+b+c) / 3 = a/3+b/3+c/3

Also im Zweifel erst mal alle durch die Anzahl teilen und aufaddieren. Da sollte man nach meiner Meinung für die Zwischenrechnung aber float/double nehmen, sonst werden die Rundungsfehler zu groß.

Oder erst mal checken, ob die Summe zu groß wird.

Gruß Tommy

Tommy56:

(a+b+c) / 3 = a/3+b/3+c/3

Also im Zweifel erst mal alle durch die Anzahl teilen und aufaddieren. Da sollte man nach meiner Meinung für die Zwischenrechnung aber float/double nehmen, sonst werden die Rundungsfehler zu groß.

Gruß Tommy

average += (double)value / size;

Hab ich so gemacht, oder verstehe ich dich falsch?

Gruß Fips

Sorry, meine Schlamperei. Ich habe nicht bis zum Ende gelesen.

Gruß Tommy

Hallo,

auf meinen AVR erhalte ich zu große Rundungsfehler. Ist bei euch anders?

template <typename T, size_t size>
T averageRead(T (&fifo)[size])
{
  double average {0};
  for (auto& value : fifo)
  {
    average += (double)value / size;
    Serial.println(average);
  }
  return (T)average;
}

void setup() {
  Serial.begin(115200); delay(100);

  uint32_t uintBuffer1[] {2134567890, 1234567890, 987654321, 613, 981};
  Serial.println("uint32: ");
  Serial.println(averageRead(uintBuffer1));
  Serial.println("Erwartet: 871358339");

  Serial.println(2134567890/5);

  
}

void loop() {}

Ausgabe:

uint32: 
426913600.00
673827200.00
871358080.00
871358208.00
871358400.00
871358400
Erwartet: 871358339
426913578

Erster Wert sollte eigentlich letzterem entsprechen.

Baue ich die for Schleife komplett um klappt es, minimaler Rundungsfehler.
Mit dem casten hat demnach seine Eier.

template <typename T, size_t size>
T averageRead(T (&fifo)[size])
{
  uint32_t average {0};

  for (byte i=0; i<size; i++)
  {
    average += fifo[i] / size;
    Serial.println(average);
  }
  return (T)average;
}

void setup() {
  Serial.begin(115200); delay(100);

  uint32_t uintBuffer1[] {2134567890, 1234567890, 987654321, 613, 981};
  Serial.println("uint32: ");
  Serial.println(averageRead(uintBuffer1));
  Serial.println("Erwartet: 871358339");

  Serial.println(2134567890/5);

  
}

void loop() {}

Ausgabe:

uint32: 
426913578
673827156
871358020
871358142
871358338
871358338
Erwartet: 871358339
426913578