Waarom gaat mijn afronding niet goed ?

algemene C code
Gebruikers-avatar
Berichten: 118
Geregistreerd: 01 Okt 2015, 11:54
Woonplaats: Castricum

Waarom gaat mijn afronding niet goed ?

Berichtdoor FonzieBonzo » 19 Mrt 2017, 23:49

Ik ben een tijdje bezig geweest met het onderzoeken van een fout in mijn programma.
Uiteindelijk kwam het doordat ik het afronden tot 1 decimaal via het voorbeeld DeFloatFout deed, maar waarom gaat dat eigenlijk niet goed ?
Ik heb al op verschillende punten er haakjes tussen gezet maar het blijft afgerond worden tot hele getallen.

Gr,
Fonzie

cpp code
void setup() {
Serial.begin(9600);
float DeFloatFout = 1.54;
float DeFloatGoed = 1.54;

DeFloatFout = round(DeFloatFout * 10) / 10;

DeFloatGoed = round(DeFloatGoed * 10);
DeFloatGoed = DeFloatGoed / 10;

Serial.println(DeFloatFout); // geeft 1.00

Serial.println(DeFloatGoed); // geeft 1.50
}

Advertisement

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: Waarom gaat mijn afronding niet goed ?

Berichtdoor Koepel » 20 Mrt 2017, 02:49

Ha ha, 1 april. Wacht even... het gaat echt verkeerd :shock: Jakkes :!: Wat een smerige bug :evil:

Het kan alleen op die manier gaan als de round() functie een integer terug zou geven en de compiler de deling met integers gaat doen.

De round() functie zit in <math.h> en daar staat:
Code: Alles selecteren
extern double round (double __x) __ATTR_CONST__;
#define roundf round
Daar geeft die functie een floating point getal terug, dat is goed.

Maar in <Arduino.h> staat:
Code: Alles selecteren
#define round(x)     ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
En dat is fout, want die geeft een integer waarde terug. Het is dan wel een long integer, maar de compiler gaat dan met integers rekenen. Als je dat resultaat gaat delen door integer "10", dan komt er "1" uit. Ook kan een floating point getal groter zijn dan een long. Diegene die dat verzonnen heeft was waarschijnlijk dronken of snapt niets van programmeren (waarschijnlijk allebei).
Ik heb het hele internet afgezocht, maar geen enkele officiële library heeft zo'n macro, alleen Arduino.

Het wordt op verschillende plaatsen gemeld, maar die bug zit er ondertussen nog steeds in.
De oplossing zou zijn om de #define in Arduino.h (dat is een 'macro') uit te schakelen door dit bovenaan je sketch te zetten:
Code: Alles selecteren
#undef round


Zulke problemen heb ik zelf trouwens niet, want ik gebruik floating point getallen voor floating point berekeningen. Een "10" is namelijk een integer en een "10.0" is een floating point getal.

Alleen al deze wijziging zorgt er voor dat het wel werkt:
Code: Alles selecteren
DeFloatFout = round( DeFloatFout * 10.0) / 10.0;


Je sketch zou op deze manier geschreven kunnen worden:
Code: Alles selecteren
// Arduino IDE 1.81 with Arduino Uno

// Undefine the round macro in Arduino.h because that is a bug.
#undef round

void setup()
{
  Serial.begin(9600);

  float DeFloatFout = 1.54;
  DeFloatFout = round( DeFloatFout * 10.0) / 10.0;
  Serial.println(DeFloatFout);

  float DeFloatGoed = 1.54;
  DeFloatGoed = round(DeFloatGoed * 10.0);
  DeFloatGoed = DeFloatGoed / 10.0;
  Serial.println(DeFloatGoed);

  float myFloat = 1.45;
  Serial.println(myFloat,1);  // Serial.print does rounding itself
}

void loop()
{
}

Gebruikers-avatar
Berichten: 118
Geregistreerd: 01 Okt 2015, 11:54
Woonplaats: Castricum

Re: Waarom gaat mijn afronding niet goed ?

Berichtdoor FonzieBonzo » 20 Mrt 2017, 10:15

Oh gelukkig maar want ik zoek natuurlijk eerst de fout bij mezelf :-)

Dat is dan wel een slordige bug idd. Zeker als het al een tijdje bekend is.
Of zullen ze het niet meer durven op te lossen omdat systemen/formules rekenen op deze bug ?

In ieder geval dank voor de uitleg, is snap nu ook waar het fout gaat....


Gr,
Fonzie

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: Waarom gaat mijn afronding niet goed ?

Berichtdoor Koepel » 20 Mrt 2017, 11:46

Issue gemaakt: https://github.com/arduino/Arduino/issues/6098.
Maar ik heb er weinig vertrouwen in dat er iets mee gedaan wordt.

Probeer maar niet om er een "intelligente" reden voor te verzinnen dat het er nog steeds in zit, want voor zo'n bezopen belachelijk domme bug bestaat natuurlijk geen goede reden.

Een floating point library hoort onder alle omstandigheden betrouwbaar en voorspelbaar te zijn. Daar is veel moeite voor gedaan, en het is volgens een bepaalde standaard. De macro in "Arduino.h" gaat er zomaar iets anders van maken. Iemand die de floating point om zeep helpt heeft echt geen benul van programmeren. Des te meer ik er over nadenk, des te slechter ik het vind dat er zo iets in is geslopen.

Dankjewel dat je een duidelijk voorbeeld gaf, zo kwam ik snel bij het probleem. Eigenlijk zou Arduino je geld moeten geven voor het vinden van zo'n bug :mrgreen: Dat zit er met Open Source helaas niet in.

Gebruikers-avatar
Berichten: 118
Geregistreerd: 01 Okt 2015, 11:54
Woonplaats: Castricum

Re: Waarom gaat mijn afronding niet goed ?

Berichtdoor FonzieBonzo » 20 Mrt 2017, 20:13

Jammer van het geld maar misschien het belangrijkste, wat wij in ons leven ooit nog gaan doen we zullen nu in de hemel terecht komen :-)

Wat ik alleen nog niet snap (en ik wil het snappen) waarom het wel goed gaat als ik het op deze manier vertel :

cpp code
DeFloatFout = round( DeFloatFout * 10.0) / 10.0;


Voor de round() die intern een integer gebuikt zou die .0 toch niet uit mogen maken?

Gr,
Fonzie

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: Waarom gaat mijn afronding niet goed ?

Berichtdoor Koepel » 20 Mrt 2017, 20:44

Een "10" in de code is een integer.
Een "10.0" in de code is een float.
Wanneer de compiler een float en integer ziet, dan maakt de compiler er een float berekening van.
Wanneer de compiler een integer en een integer ziet, dan maakt de compiler er een integer berekening van.

Dus als ik het tot het bare minimum reduceer, dan is dit al voldoende:
Code: Alles selecteren
DeFloatFout = round( DeFloatFout * 10) / 10.0;
Is dat wat je bedoelt ?
Maar waarom half werk doen ;)
Ik gebruik niet alleen "10.0" voor de compiler, maar ook om mezelf er aan te herinneren dat het een floating point berekening is.

Hieronder een voorbeeld van hoe het vooral niet moet:
Code: Alles selecteren
// Arduino IDE 1.81 with Arduino Uno

// The "#undef round" removes the round() macro in Arduino.h
#undef round

void setup()
{
  Serial.begin( 9600);
  Serial.println(F( "Example how NOT to mix integer and float"));

  int i = 50;
  int j = 3;
  float f = 12.5;
  float g = 1.085E+6;

  float result1 = g * (f * ( i / j ));
  float result2 = g * (f * ( float(i) / float(j)));

  Serial.println( result1);
  Serial.println( result2);
}

void loop()
{
}

Zie je die "( i / j )", die staat tussen haakjes en wordt dus eerst uitgevoerd. Die berekening doet de compiler met integers, pas daarna de "(f * " is vermenigvuldigen met een float en dan wordt het pas een float berekening.
Voorkomen is beter dan genezen.
Laatst gewijzigd door Koepel op 20 Mrt 2017, 20:57, in totaal 1 keer gewijzigd.

Gebruikers-avatar
Berichten: 5043
Geregistreerd: 13 Mei 2013, 20:57
Woonplaats: Heemskerk

Re: Waarom gaat mijn afronding niet goed ?

Berichtdoor nicoverduin » 20 Mrt 2017, 20:48

FonzieBonzo schreef:Jammer van het geld maar misschien het belangrijkste, wat wij in ons leven ooit nog gaan doen we zullen nu in de hemel terecht komen :-)
nou daar heb ik ook niks aan als anti gelovige:)
Docent HBO Technische Informatica, Embedded ontwikkelaar & elektronicus
http://www.verelec.nl

Berichten: 4064
Geregistreerd: 16 Okt 2013, 14:31
Woonplaats: s hertogenbosch

Re: Waarom gaat mijn afronding niet goed ?

Berichtdoor shooter » 20 Mrt 2017, 21:44

fons begin eerst eens met variabelen maar 1 keer te gebruiken, dat is simpel en voorkomt problemen met int,float etc.
verder heeft koepel natuurlijk wel gelijk, de compiler weet best hoe het moet als je het hem ook maar netjes verteld.
paul deelen
shooter@home.nl

Gebruikers-avatar
Berichten: 118
Geregistreerd: 01 Okt 2015, 11:54
Woonplaats: Castricum

Re: Waarom gaat mijn afronding niet goed ?

Berichtdoor FonzieBonzo » 20 Mrt 2017, 23:24

Ok ik snap het nu, thanks all !

Gr,
Fonzie

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: Waarom gaat mijn afronding niet goed ?

Berichtdoor Koepel » 22 Mrt 2017, 13:49

Mijn "issue" heeft een reactie: https://github.com/arduino/Arduino/issues/6098#issuecomment-288371069
[EDIT] Hij zegt nu 'sorry'. Er wordt naar gekeken :D

Volgende

Terug naar C code

Wie is er online?

Gebruikers in dit forum: Geen geregistreerde gebruikers en 14 gasten