Arduino Forum

Using Arduino => Microcontrollers => Topic started by: dsyleixa on Apr 29, 2019, 08:04 pm

Title: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: dsyleixa on Apr 29, 2019, 08:04 pm
hi,
my ESP32 calculates floats faultily opp. to my M4 and my M3/Due
(Arduino IDE 1.8.8 )

e.g, after a long calculation (1264 steps) by the Feather M4 I get the result 32.843750,
whilst the Feather ESP32 calculates 54196.625
update:
I now retested by the Due, and here I also get  the result 32.843750, just like the M4.

:o

this is the relevant code:

Code: [Select]

//  Lunar Lander

 
// preprocessor defaults for time sync:
//#define SYNC_REALTIME      // real time sync; outcomment for time lapse


#if defined (SAM)
   #include "avr/dtostrf.h"   // sprintf() and dtostrf() for floats
#endif


//----------------------------------------------------
// flight control
//----------------------------------------------------
// public:
float mFuel=8200;          // fuel mass in kg for landing
float hi=15300;            // act height in m
float vHorz=1685;          // Horizontal orbital speed m/s

float sTargm=470000;       // horizontal way to target landing place

float burnPerc=0;          // user input: burnrate %
float ftilt;               // tilt horiz...vert -1...0...+1
float tiltDeg;             // tilt degrees -90°...0...+90°

//----------------------------------------------------
// private:
float ti=0.0, dt=0.5;       // act time, delta time in sec

const float g=1.62;               // Moon gravity
const float MoonRad=3476000/2.0;  // Moon radius
const float mLander=6500;         // lander mass in kg with launch fuel
const float Isp=3050;             // Rocket engine Specific Impulse
const float FBrake=45000;         // Rocket engine max Propulsion Force

float burnMax=FBrake/Isp;   // absolute max fuel burnrate
float mTotal=mLander+mFuel; // brutto weight with full tanks 
float rBrake;               // user Rocket brake force 100%, percentual
float dFuel=0;              // delta fuel
float burnf=0;              // burnrate factor 0.0 ... 1.0

float dh=0.0;               // delta height in m
float scaleH=hi/100;        // scaler for tft.hight=100%
float vVert=0.0;            // Impact speed in m/s   
float accVert=0;            // vertical accel (sum)
float accBrake=0;           // acc by break rockets
float fCentrifug=0;         // centrifugal force by orbital speed
float accCentrif=0;         // centrifugal accel by orbital speed

float sHorzm=0.0;           // horizontal way flown in m



//----------------------------------------------------
// Serial LogBook
//----------------------------------------------------
void LogBook(){
   char sbuf1[50], sbuf2[50] ;   
   char* headline1 = "t.sec  hi.m vVert vHoriz   ";
   char* headline2 = "Burn tilt    brake acc  Fuel TBase.m";
   
   Serial.print(headline1);
   Serial.println(headline2);
   
   sprintf(sbuf1,    "%5.1f %5d %4d  %4d     ",
                    ti, (int)hi, (int)vVert, (int)vHorz);
   sprintf(sbuf2,    "%3d%% %4d     %3.1f %4.1f %5d %f",
                    (int)burnPerc, (int)(ftilt*90), accBrake, accVert,
                    (int)mFuel, (float)(sTargm-sHorzm));
                   
   Serial.print(sbuf1); Serial.println(sbuf2); 
   Serial.println(); 
}



uint32_t dtime;

//----------------------------------------------------
// Lander Move
//----------------------------------------------------
void LanderMove() { 
   static float t0=ti;     
   
   dtime=millis();
   if(hi>0) {

      // Burn Ratio:
      // 100 = 100% == full brake power
      //  50 =  50% == half brake power
      //  0  =  0%  == zero brake power
      //  or anything in between
      //
      // 100% BURN RATIO (BRAKE  POWER)
      //     => 45000kN propulsion force
      //     => 14,75 kg Fuel burn per second
      //     => brake accelation = 3m/s²
     
      // XEROX Board Computer program, debug:   
      if( sHorzm<15600.0) burnPerc=0;  //  sHorzm<15600   
      else
      if( vVert>60||vHorz>30 ) burnPerc=100;  // 
      else
      if(vVert>50) burnPerc=65;
      else       
      if(vVert>40&&hi<3000) burnPerc=45;
      else
      if(vVert>35&&hi<1800) burnPerc=40;
      else
      if(vVert>10&&hi<1100) burnPerc=35;
      else
      if(vVert>=1&&hi<120) burnPerc=33;
      else     
      burnPerc=0;

      // calculate control

      fCentrifug=vHorz*vHorz*mTotal/(MoonRad+hi);
      accCentrif=fCentrifug/mTotal;
     
      if(mFuel==0) burnPerc=0;          // no fuel, no burn ;)     
      burnf = burnPerc/100.0;           // factor  0...1

  //  vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
      dFuel=  burnMax*burnf*dt;        // try burnrate: enough fuel?
      dFuel = min(dFuel, mFuel);       // calc available rest fuel
     
      burnf = (dFuel/burnMax)/dt;       // re-calc burnrate by rest fuel
      burnPerc = burnf*100;
     
      rBrake= FBrake*burnf;           // rel brake force 
      accBrake= rBrake/mTotal;        // rocket brake acceleration
     
      mFuel = max(mFuel-dFuel, (float)0);     // rest fuel >=0         
      mTotal= mLander+mFuel;          // new total mass   
 
      if(vVert>=5  ) {
        if(vVert>=40 && vHorz>2 )  ftilt=0.65; // 58.5°
        else
       
        if(vVert>=30 && vHorz>2 )  ftilt=0.75; // 67.5°         
        else
        if(vVert>=20 && vHorz>2 )  ftilt=0.80; // 72.0°                 
 
        else
        if(vHorz<=2 && vHorz>=-2) ftilt=0.0;
        else       
        if(vHorz<0)    ftilt=0.0;
   
        else ftilt=0.85;    // 76.5°         
      }
      else
      if(vVert<5&&vHorz>20 )  ftilt=1;
      else ftilt=0;

      accVert = g - (1-abs(ftilt))*accBrake -accCentrif;
      vVert = vVert + accVert*dt;        // fractional vertical brake
     
      if(vHorz>0)
      vHorz = vHorz - (ftilt)*accBrake*dt;   // fractional horizonal brake

      //  vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
      sHorzm = sHorzm + vHorz*dt;            // horizontal way flown     
      dh = 0.5*accVert*dt*dt + vVert*dt ;    // delta height by res. grav+centrifug.+brake acc.
      hi = hi-dh;                            // new resulting height

      //-----------------------------------------------         
      // pause
      #ifdef SYNC_REALTIME 
         while( millis()-dtime < dt*1000 ); 
      #endif   
      //-----------------------------------------------
               
      ti+=dt;       
      LogBook();
      t0=ti;           
   
      //-----------------------------------------------
      // Landing specs/ratings
     
                                                     
      if ( (( hi<=0 && vVert>=5 ) && vVert<8) )   // Damage             
      { 
        Serial.println();
        Serial.println(" !! Damage !!");   
   
      }
     
      else       
      if ( hi<=0 && vVert<5 && abs(sTargm-sHorzm)>100) {  // very good Landing but way off
        burnPerc=0;               
        Serial.println();
        Serial.println("Very good but way off!");   
      }

      else       
      if ( hi<=0 && vVert<5) {               // Perfect Landing
        burnPerc=0;               
        Serial.println();
        Serial.println("Perfect Landing!");     
      }


      else 
      if ( hi<=0 )        // B L A S T
      { 
        Serial.println();
        Serial.println(" !!! B L A S T !!!");         
      }
   }   
}


//----------------------------------------------------
// setup
//----------------------------------------------------
void setup() {
  #if defined (SAM)
     asm(".global _printf_float"); // sprintf() and dtostrf() for floats
  #endif
 
  Serial.begin(115200);
  delay(2000);
  Serial.println("Serial started!");

  sHorzm=0; // way flown
 
  Serial.println();
   
  delay(dt*1000); 
 
  fCentrifug=vHorz*vHorz*mTotal/(MoonRad+hi);
  accCentrif=fCentrifug/mTotal;
  accVert=g-accBrake-accCentrif;
  LogBook(); 
}


//----------------------------------------------------
// loop
//----------------------------------------------------   
void loop(void) { 
         
   if (hi>0)  {
      LanderMove();     
   }
 
}



the questionable value is the last one in either Serial output line (TBase.m)
actually the M4 == DUE result
32.843750
is probably  the correct one...!

-------------------------
edit, update:
could be confirmed for MEGA2560 too, also 32.843750 here!

so just faulty for ESP32, both for fp32 and fp64 double as meanwhile tested.
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: dsyleixa on Apr 30, 2019, 08:32 am
Has anyone ever heard or read such a thing and perhaps can check the calculation results and  possibly confirm either one  for M3, M4 and/or ESP32 platforms?
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: dsyleixa on May 01, 2019, 08:20 am
Meanwhile, the fact that a ESP32 evaluate differently (i.e. 54196.625) from Due, M4, and Mega2560 (i.e., all 32.843750) was confirmed by another user in another forum.
Quote
(ESP32)
TBase.m
54196.625000
Anyone else here to test the code from above with a proprietary ESP32 vs. a Due or a M4?
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: srnet on May 01, 2019, 08:35 am
Anyone elese here to test the code from above with a proprietary ESP32 vs. a Due or a M4?
Perhaps ask in the Expressif forums if you dont get an answer here.

Its their code.
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: dsyleixa on May 01, 2019, 08:46 am
yes I will do so as soon as there are more people who could confirm that issue on either MCU.

meanwhile a 2nd user could confirm my result, that M4 (identically to  Due and Mega2560) evaluate to 32.843750 , differently to ESP32 (54196.625):
 
Quote
Running on a Feather M4 Express, the final value of TBase.m was 32.843750.
So I would appreciate very much if more people could  test the code from above with a proprietary ESP32 vs. a Due or a M4...!
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: MartinL on May 01, 2019, 03:06 pm
Hi dsyleixa,

The Arduino Zero (ARM Cortex M0+) agrees with the other ARM microcontrollers and evaluates to 32.843750. The ESP32 (WROOM32) generates 54196.625.
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: dsyleixa on May 01, 2019, 06:43 pm
Hi dsyleixa,

The Arduino Zero (ARM Cortex M0+) agrees with the other ARM microcontrollers and evaluates to 32.843750. The ESP32 (WROOM32) generates 54196.625.

thank you very much!
How do you think about that issue?
Actually it's not quite reasonable that ESP32 evaluates differently to (all?) other boards...?


Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: MartinL on May 01, 2019, 07:20 pm
Hi dsyleixa,

I can only imagine that it has something to do with the ESP32's compiler. The results from the ARM and ESP32 boards seem to gradually diverge that may suggest some error in precision.

I noticed that problem still persists if the float data types are coverted to double.
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: dsyleixa on May 01, 2019, 07:21 pm
has the ESP32 got a hardware fpu coprocessor, like the M4?
or just software-float computations?

PS, FYI:
I just reported that already at the Espressive github repo:
https://github.com/espressif/esp-idf/issues/3405

nonetheless, more testers are highly appreciated! 8)

Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: MartinL on May 01, 2019, 08:39 pm
Quote
has the ESP32 got a hardware fpu coprocessor, like the M4?
or just software-float computations?
I believe the ESP32 does have a single precision FPU, although there doesn't appear to be much information about it.

I found this in the ESP32-IDF (IoT Development Framework) documentation:

Quote
5.9.6 Floating Point Aritmetic
The ESP32 supports hardware acceleration of single precision floating point arithmetic (float) via Floating Point
Units (FPU, also known as coprocessors) attached to each core. The use of the FPUs imposes some behavioral
restrictions on ESP-IDF FreeRTOS.
ESP-IDF FreeRTOS implements Lazy Context Switching for FPUs. In other words, the state of a core's FPU registers
are not immediately saved when a context switch occurs. Therefore, tasks that utilize float must be pinned to a
particular core upon creation. If not, ESP-IDF FreeRTOS will automatically pin the task in question to whichever core
the task was running on upon the task's first use of float. Likewise due to Lazy Context Switching, interrupt service
routines must also not use float.
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: dsyleixa on May 01, 2019, 10:05 pm
hm - thank you, interesting - nonetheless, the issue is also about fp64, but that would probably not affect the 32bit-fpu.
::?
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: MartinL on May 02, 2019, 10:50 am
Hi dsyleixa,

I managed to track down the bug.

After comparing the results from my Arduino Uno and ESP32 on a spreadsheet, I noticed that there was a spurious reading for the vertical acceleration (accVert variable) on the ESP32 during the 105th iteration.

It turns out that absolute abs() function in C should only be used with integers. However, it appears that while the AVR and ARM abs() function also accepts float data types, the ESP32 doesn't.

Replacing the abs() function for the equivalent floating point fabsf() function instead solves the issue. The AVR, ARM and ESP32 are now in agreement.
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: westfw on May 02, 2019, 12:35 pm
Nice work!  I was suspecting one of the "questionable " Arduino macros like abs(), but wasn't sure how that would fail...
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: dsyleixa on May 02, 2019, 09:14 pm
+1
I double that!
Great work, congratulations , and many thanks for your efforts
8) 8) 8)!
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: zoomx on May 03, 2019, 03:45 pm
https://github.com/arduino/reference-en/issues/362#issuecomment-478540338 (https://github.com/arduino/reference-en/issues/362#issuecomment-478540338)

Quote
I took a closer look into this issue and found that it's a bit complicated. The standard C++ and C implementations of abs() indeed don't work with float. However, Arduino has replaced that with their own macro implementation in their hardware cores:
https://github.com/arduino/ArduinoCore-avr/blob/1.6.23/cores/arduino/Arduino.h#L87-L94 (https://github.com/arduino/ArduinoCore-avr/blob/1.6.23/cores/arduino/Arduino.h#L87-L94)


Code: [Select]
#define abs(x) ((x)>0?(x):-(x))

which does work with any type. This macro will be used in all .ino files, and any other source file that #includes Arduino.h.
So maybe the ESP32 core doesn't have this macro.
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: MartinL on May 03, 2019, 04:40 pm
Quote
So maybe the ESP32 core doesn't have this macro.
I had a peek at the ESP32's "Arduino.h" file, it uses the same macro.
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: dsyleixa on May 03, 2019, 05:17 pm
https://github.com/arduino/reference-en/issues/362#issuecomment-478540338 (https://github.com/arduino/reference-en/issues/362#issuecomment-478540338)
So maybe the ESP32 core doesn't have this macro.
I had a peek at the ESP32's "Arduino.h" file, it uses the same macro.
no, although the macro existed, the ESP32 worked with standard <math.h> C code which requires fabs() for double and fabsf()  for float, basically like gcc for Linux too (which produce the same  faulty values by abs).

Quote
The weird thing is, the Arduino core on ESP32 has an abs() macro as well:
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/Arduino.h#L76

So there is probably a bug in the Arduino core for ESP32 here, where it isn't using the "Arduino-ified" abs() macro for some reason.
https://github.com/espressif/esp-idf/issues/3405#issuecomment-488872389

If you look at the github discussion, that could be resolved by a PR using std::abs() instead, which works correctly for either value type.
Instead, using Arduino macros for abs() is actually a bad practice.
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: MartinL on May 03, 2019, 06:49 pm
Thanks for the link, I followed it through to the pull request: https://github.com/espressif/arduino-esp32/pull/2738 (https://github.com/espressif/arduino-esp32/pull/2738).

So seems that the ESP's abs() macro in the "Arduino.h" file is being subsequently overriden by another redefinition of macro. This redefinition is in the included <algorithm> file (which in turn calls on the <cstdlib>), a number of lines further down. The standard library's abs() doesn't process float data types.

This explains why the abs() macro in the "Arduino.h" file exists, but is never used.
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: dsyleixa on May 03, 2019, 07:53 pm
Quote
The standard library's abs() doesn't process float data types.
You are right, I stand corrected - I was confused by a chart table

abs() for fp is only since C++17, not for C++11 or C++14:
Quote
(since C++17)
float       abs( float arg );
  (1)   
double      abs( double arg );
Quote
std::abs(float), std::fabs, std::fabsf, std::fabsl

C++

Numerics library

Common mathematical functions

Defined in header <cmath>

Defined in header <cstdlib>

(since C++17)
float       abs( float arg );
(1)
double      abs( double arg );
(2)
long double abs( long double arg );
(3)
Defined in header <cmath>

(4)
float       fabs ( float arg );

float       fabsf( float arg );
(since C++11)
double      fabs ( double arg );
(5)
(6)
long double fabs ( long double arg );

long double fabsl( long double arg );
(since C++11)
double      fabs ( IntegralType arg );
(7) (since C++11)
https://en.cppreference.com/w/cpp/numeric/math/fabs

Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: MartinL on May 04, 2019, 10:07 am
Hi dsyleixa,

Thanks for investigating, that's interesting, I think it makes sense that they're chosing to effectively overload the abs() function, so that it will operate with float data types in C++17 in the future.

In the ESP32's "Arduino.h" file, I noticed that they moved the min() and max() macros to the bottom of the file (after the <algorithm> include). If the Arduino abs() macro definition:

Code: [Select]
// undefine stdlib's abs if encountered
#ifdef abs
#undef abs
#endif

#define abs(x) ((x)>0?(x):-(x))

 ...is also moved to the bottom of the "Arduino.h" file as well, it effectively overrides the standard library's abs() function and your LunarLander code works with abs() oncemore.

In other words the Arduino abs() macro itself works just fine, it's just that it's currently in the wrong place in the ESP32's "Arduino.h" file.
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: MartinL on May 04, 2019, 10:38 am
Looks like projectgus's pull request on Github also solves the issue, just by calling on:

Code: [Select]
using std::abs;
...in the "Arduino.h" file and deleting the Arduino macro. This redefines abs() using the C++ standard library function.
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: dsyleixa on May 05, 2019, 04:43 pm
Looks like projectgus's pull request on Github also solves the issue, just by calling on:

Code: [Select]
using std::abs;
...in the "Arduino.h" file and deleting the Arduino macro. This redefines abs() using the C++ standard library function.
yes, but my point was:
C++11 and C++14 also have std::abs just for ints and std::fabs and std::fabsf for floats !
Just C++17 has std::abs  both for ints and fps...
but does Arduino actually use C++17 ...?
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: pert on May 05, 2019, 08:42 pm
Arduino SAMD Boards is currently using C++11:
https://github.com/arduino/ArduinoCore-samd/blob/master/platform.txt#L42 (https://github.com/arduino/ArduinoCore-samd/blob/master/platform.txt#L42)

The toolchain version currently in use doesn't even support C++17. However, there are plans to update the toolchain and use C++17 in the near future:
https://github.com/arduino/ArduinoCore-samd/issues/313 (https://github.com/arduino/ArduinoCore-samd/issues/313)

The ESP32 core for Arduino is also using C++11:
https://github.com/espressif/arduino-esp32/blob/master/platform.txt#L31 (https://github.com/espressif/arduino-esp32/blob/master/platform.txt#L31)
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: MartinL on May 05, 2019, 10:16 pm
Quote
C++11 and C++14 also have std::abs just for ints and std::fabs and std::fabsf for floats !
Just C++17 has std::abs  both for ints and fps...
but does Arduino actually use C++17 ...?
Yes, it's really confusing. I was also wondering why simply adding the "using std::abs;" line, allows the abs() function to work with floats, although the ESP32 is using C++11 (and not C++17).

After some investigation, I believe that the C++11's <cmath> file which is included in "Arduino.h", undefines the "abs" macro and redefines it as an overloaded function "std::abs" for a number of data types including floats.

The "using std::abs;" line allows "abs" to be used in lieu of any predefined macros, so that it isn't necessary to call "std::abs" in your sketch, but simply use "abs" instead.
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: dsyleixa on May 06, 2019, 09:30 am
Yes, it's really confusing. I was also wondering why simply adding the "using std::abs;" line, allows the abs() function to work with floats, although the ESP32 is using C++11 (and not C++17).

After some investigation, I believe that the C++11's <cmath> file which is included in "Arduino.h", undefines the "abs" macro and redefines it as an overloaded function "std::abs" for a number of data types including floats.

The "using std::abs;" line allows "abs" to be used in lieu of any predefined macros, so that it isn't necessary to call "std::abs" in your sketch, but simply use "abs" instead.
it appears as if gpp already features the std::abs() for fp since C14 or even 11, opposite to C++ standards only since 17
https://gcc.godbolt.org/z/Flkq60
https://en.cppreference.com/w/cpp/numeric/math/fabs
https://github.com/espressif/esp-idf/issues/3405#issuecomment-489480050
Title: Re: ESP32 calculates floats faultily opp. to my M4 and my M3/Due
Post by: MartinL on May 06, 2019, 12:51 pm
Quote
it appears as if gpp already features the std::abs() for fp since C14 or even 11, opposite to C++ standards only since 17
https://gcc.godbolt.org/z/Flkq60
https://en.cppreference.com/w/cpp/numeric/math/fabs
https://github.com/espressif/esp-idf/issues/3405#issuecomment-489480050
Thanks for the links regarding the GCC and C++.

Yes, it appears to be a complex mix of Arduino, compiler and C/C++ standards. Thanks for flagging this up on Github. At least situation is now sorted, or a least will be once ESP32's "Arduino.h" is officially updated.