Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 22 of 22

Thread: How to print 64bit float in teensyduino for teensy 4.1

  1. #1
    Junior Member
    Join Date
    Dec 2020
    Posts
    6

    How to print 64bit float in teensyduino for teensy 4.1

    Hi,

    I'm trying to print a float64 number with more than 7 decimals , but without any success. Whatever I try , I'm not getting more than 7 decimals.
    This is my code :

    //--------------------------------
    //64 bit float test for Teensy 4.1
    //--------------------------------

    #include <arm_math.h>

    void setup() {
    // put your setup code here, to run once:
    Serial.begin(9600);
    }

    void loop() {
    // put your main code here, to run repeatedly:
    double number1 = 1.00000001L;
    double number2 = 1.000000005L;
    float number3 = 22.0L;
    float number4 = 7.0L;
    Serial.printf("%d\n", (number1 f+ number2));
    Serial.printf("%lf\n", number3/number4);

    }

    I'm reading on many internet searches that arduino IDE can only handle 32 bit floats . Is this true ??

    Any help welcome !
    Regards,
    Ronny

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,505
    This works for 64 bit values as used here for integers: >> "%llu"

    Code:
    	Serial.printf("Bytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize());

  3. #3
    Junior Member
    Join Date
    Dec 2020
    Posts
    6
    Hi defragster

    This gives a strange result , changed code to :

    //--------------------------------
    //64 bit float test for Teensy 4.1
    //--------------------------------

    #include <arm_math.h>

    void setup() {
    // put your setup code here, to run once:
    Serial.begin(9600);
    }

    void loop() {
    // put your main code here, to run repeatedly:
    double number1 = 1.00000001L;
    double number2 = 1.000000005L;
    float number3 = 22.0L;
    float number4 = 7.0L;

    Serial.printf("%llu\n", number3/number4);

    result is : 4614259503775350784

  4. #4
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,995
    The format specifier for double is %lf
    For float it is %f
    So, your printf is in double way wrong number 3 and number 4 are floats, so the result is float (-> use %f) ... %llu is for unsigned long long

    %1.9lf prints your 1.000000005 correctly.

    For printf format specifiers: Google is your friend

  5. #5
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,505
    Indeed that was noted for INTEGER

    This link suggests %lf should work for DOUBLE where on Teensy "%f" is float {unlike the code in use for the post where double is assumed}:: stackoverflow.com/questions/4264127/correct-format-specifier-for-double-in-printf

  6. #6
    Junior Member
    Join Date
    Dec 2020
    Posts
    6
    Hi Frank ,

    almost there , but not correct results. it looks like calculation is done in 32 bit (?)

    //--------------------------------
    //64 bit float test for Teensy 4.1
    //--------------------------------

    #include <math.h>

    void setup() {
    // put your setup code here, to run once:
    Serial.begin(9600);
    }

    void loop() {
    // put your main code here, to run repeatedly:
    float number1 = 1.00000001L;
    float number2 = 1.000000005L;
    float number3 = 22.0;
    float number4 = 7.0;

    Serial.printf("%1.9lf\n", number1+number2); // prints 2.000000000 -> wrong !
    Serial.printf("%1.9lf\n", number3/number4); // prints 3.142857075 -> wrong !

    Any ideas remaining ... ?
    Ronny

  7. #7
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,546
    You've declared them all as float. The addition and division are done as float with the result having float precision which is only about 7 digits.
    Declare them as double or cast them.

    Pete

  8. #8
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,546
    This "%1.9lf" might cause confusion too. If you want double it should be something like "%24.20lf".

    Pete

  9. #9
    Junior Member
    Join Date
    Dec 2020
    Posts
    6
    Hi Pete ,

    Thanks , this finally works !!!
    Most forums state that float and double is the same , but apparently it isn't.
    This double and float and long are somewhat confusing me.
    Thanks again and also thanks to the other members for their help.

    Wishes ,
    Ronny

  10. #10
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,546
    float and double are the same on most Arduinos and Teensys before 4.0 because "double" arithmetic is very slow on processors which don't have the double precision hardware. It also saved precious ram space on early 8-bit Arduinos (Uno, Nano) and Teensys (up to T++2).
    But Teensy4.x have double precision arithmetic in hardware and lots of ram.

    Pete

  11. #11
    Junior Member
    Join Date
    Dec 2020
    Posts
    6

    Thanks ! That explains something.

    Ronny

  12. #12
    Quote Originally Posted by manes6969 View Post
    Hi Pete ,

    Thanks , this finally works !!!
    Most forums state that float and double is the same , but apparently it isn't.
    This double and float and long are somewhat confusing me.
    Thanks again and also thanks to the other members for their help.

    Wishes ,
    Ronny
    Could you share how your sketch finally turned out?

  13. #13
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,786
    I tried this just now.

    Code:
    void setup() {
      Serial.begin(9600);
    }
    
    void loop() {
      double number1 = 1.00000001L;
      double number2 = 1.000000005L;
      double number3 = 22.0;
      double number4 = 7.0;
    
      Serial.printf("%12.9lf\n", number1 + number2);
      Serial.printf("%12.9lf\n", number3 / number4);
      Serial.println();
      delay(1000);
    }
    Running on a Teensy 4.1, it prints this in the serial monitor

    Code:
     2.000000015
     3.142857143
    
     2.000000015
     3.142857143
    
     2.000000015
     3.142857143
    
     2.000000015
     3.142857143
    
     2.000000015
     3.142857143
    
     2.000000015
     3.142857143

  14. #14
    It's fine when you define the double variable, as in:
    Code:
    double Lat = 1.00000001L
    But when that value comes from another source, such as some data thrown by a sensor or by some previous mathematical operation, how is it possible to preserve the value of X as double?.

    Code:
    #include "ILI9341_t3.h"
    #include <SPI.h>
    
    #define TFT_RST  8
    #define TFT_DC   9
    #define TFT_CS  10
    ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, TFT_RST);
    
    #define TFT_RST2 21 
    #define TFT_DC2  20
    #define TFT_CS2  15
    ILI9341_t3 tft2 = ILI9341_t3(TFT_CS2, TFT_DC2, TFT_RST2);
    
    
    char floatTXT[40];
    
    void setup ()
    {
      Serial.begin (115200);
      Serial.println ();
    
      tft.begin();
      tft.setRotation(3); 
      tft.fillScreen(ILI9341_BLUE);
      tft.setTextColor(ILI9341_WHITE);
      tft.setTextSize(1);
      tft.setCursor(320-80,10);
      tft.print("First TFT");
      
      tft2.begin();
      tft2.setRotation(3);
      tft2.fillScreen(ILI9341_RED);
      tft2.setTextColor(ILI9341_WHITE);
      tft2.setTextSize(1);
      tft2.setCursor(320-80,10);
      tft2.print("Second TFT"); 
      
      double Lat= 1.00000001L, Long= 1.000000005L;
              
      tft.setTextSize(2);
      Serial.println();
      Serial.print ("(");  Serial.print (Lat,9);  Serial.print (", ");   Serial.print (Long,9);  Serial.print (")");  
      tft.setCursor(0,50);  tft.print("(");  tft.print(Lat,9);  tft.print(", ");  tft.print(Long,9);  tft.print(")");
    
      double XLat, YLong;  //sensor 
      XLat= 1.00000001;  YLong= 1.000000005;
      Lat=XLat; Long=YLong;
        
      Serial.println();
      sprintf(floatTXT,"(%.9f, %.9f)", Lat, Long);
      Serial.println (floatTXT);
      tft.setCursor(0,75);   tft.print(floatTXT);
    }
    void loop(){}


    Code:
    double Lat= 1.00000001L, Long= 1.000000005L;
    Works fine!

    Code:
    double XLat, YLong;  //sensor 
     XLat= 1.00000001;  YLong= 1.000000005;
     Lat=XLat; Long=YLong;
    Yes please, Donīt work!

    Is it possible to add by code the ending "L" to a double value obtained from a sensor or from some mathematical operation within the sketch itself?

    PS: Excuse me for asking so much, but there are things that not even Google San understands when you ask!

  15. #15
    Ups, sorry Paul!, THX test again!

  16. #16
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,786
    Maybe you're using Teensy LC or 3.x?

    On those boards, constants default to 32 bit float, even if you assign them to 64 bit double.

    This is done to prevent casually typed float constants (without a trailing "f") from being treated at 64 bits and automatically converting all the surrounding math up to 64 bits. The FPU on Teensy 3.5 & 3.6 only handles 32 bit float, and on LC & 3.2 with no FPU, software is much faster at 32 bit float.

    On Teensy LC & 3.x, you need to add the "L" for a constant to be treated as 64 bits. Yes, it's against the C standard and it's inconvenient, but overall the trade-off is most people use only 32 bits and this prevents very common usage from spoiling 32 bit float performance.

  17. #17
    In teensy 3.6 it doesn't work, but in teensy 4 it does:



    Code:
      double XLat, YLong;  //sensor 
      XLat= 1.00000001;  YLong= 1.000000005;
      Lat=XLat; Long=YLong;
        
      Serial.println();
      Serial.printf ("(");  Serial.printf ("%.9lf",Lat);  Serial.printf (", ");   Serial.printf ("%.9lf",Long);  Serial.printf (")");  
      sprintf(floatTXT,"(%.9lf, %.9lf)", Lat, Long);
      tft.setCursor(0,75);   tft.print(floatTXT);
    Sure enough, you're right, I was working the code on a teensy 3.6. When implemented in the teensy 4 everything goes as you have indicated. Thank you.

  18. #18
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,786
    Try changing this:

    Code:
      XLat= 1.00000001;  YLong= 1.000000005;
    to this:

    Code:
      XLat= 1.00000001LL;  YLong= 1.000000005LL;
    It will work on Teensy 3.6 when you get the syntax right.

  19. #19
    To take advantage of the topic started by manes6969, I took what I learned.

    I moved towards improving the code for the GPS with what was exposed here. Previously I used only float, because the use of double was not clear to me.

    Teensy 4 + NHD FT813 3.5"


    The first line uses double correction, the second line uses float.

    Code:
      double Lat, Long;
      Lat= 19.485155;  Long= -99.831861;  //GPS
        
      //Serial.println();
      //Serial.printf ("(");  Serial.printf ("%.9lf",Lat);  Serial.printf (", ");   Serial.printf ("%.9lf",Long);  Serial.printf (")");  
      sprintf(floatGPS,"(%.9lf,%.9lf)", Lat, Long);
      GD.cmd_text(GD.w / 2, GD.h / 2, 28, OPT_CENTER, floatGPS);
    
      float FLat = Lat, FLong = Long;
      sprintf(floatGPS,"(%.9lf,%.9lf)", FLat, FLong);
      GD.cmd_text(GD.w / 2, (GD.h / 2)+25, 28, OPT_CENTER, floatGPS);
    Float always introduces errors in digits beyond the sixth, which makes it difficult to implement complex calculations, such as determining the distance between two pairs of coordinates.

    THX again

  20. #20
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,980

    Cool

    And note, I believe the code will not work on Teensy 4.0/4.1 if you optimize for space, since the sprintf when optimizing for space omits the floating point formatting stuff. Also of course, it might not work on other processors using Arduino framework.

    To that end, I wrote this fragment of code for my power meter that uses an INA219 sensor to read the amount of power used:
    Code:
    static const int	BUFFER_SIZE		= 40;
    static const unsigned	MAX_COUNTER		= (500 / DEBOUNCE);
    static unsigned int	display_counter		= MAX_COUNTER;
    
    // prevent compiler from optimizing sprintf test
    volatile float		ten			= 10.0f;
    
    // Convert a floating point number to a string with a %6.3f format.  Not all
    // versions of sprintf support floating point, so on the first call test to see
    // if it works, and if we don't get the expected result, do the conversion in
    // an alternate fashion.  This function is optimized for the range of values we
    // expect to see with the power meter.
    //
    // On the Teensy, if you optimize for space, a smaller library is used that
    // does not do have %f, %g, etc. formats.
    
    char *
    float_to_string (float number, char *buffer, int fract)
    {
      static bool first_time	= true;
      static bool sprintf_works;
    
      if (first_time) {
        first_time = false;
        strcpy (buffer, "no results");
        sprintf (buffer, "%6.3f", ten);
        sprintf_works = (strcmp (buffer, "10.000") == 0);
        if (!sprintf_works)
          Serial.printf ("sprintf does not work for floating point, '%s'\n", buffer);
      }
    
      if (sprintf_works)
        sprintf (buffer, "%*.*f", fract + 3, fract, number);
    
      else {
        bool negative	= (number < 0.0f);
        float number2	= fabs (number);
    
        if (fract == 0) {
          number2 += 0.5f;
          sprintf (buffer, "%2d.", (int) (negative ? -number2 : number2));
    
        } else if (fract == 1) {
          int number_x10	= (int) ((number2 * 10.0f) + 0.5f);
          int number_main	= number_x10 / 10;
          int number_fract	= number_x10 % 10;
    
          sprintf (buffer,
    	       "%2d.%c",
    	       negative ? -number_main : number_main,
    	       number_fract + '0');
    
        } else if (fract == 2) {
          int  number_x100	= (int) ((fabsf (number) * 100.0f) + 0.5f);
          int  number_main	= number_x100 / 100L;
          int  number_fract	= number_x100 % 100L;
    
          sprintf (buffer,
    	       "%2d.%c%c",
    	       negative ? -number_main : number_main,
    	       ((number_fract / 10)  % 10) + '0',
    	       ((number_fract / 1)   % 10) + '0');
    
        } else {
          int  number_x1000	= (int) ((fabsf (number) * 1000.0f) + 0.5f);
          int  number_main	= number_x1000 / 1000;
          int  number_fract	= number_x1000 % 1000;
    
          sprintf (buffer,
    	       "%2d.%c%c%c",
    	       negative ? -number_main : number_main,
    	       ((number_fract / 100) % 10) + '0',
    	       ((number_fract / 10)  % 10) + '0',
    	       ((number_fract / 1)   % 10) + '0');
        }
      }
    
      return buffer;
    }
    And while using a suffix of 'L' will work on the Teensy 3.x/LC, it technically is not a double constant. The 'L' suffix says the constant is long double instead of double. So if you are in a context where a double is wanted, the C++ compiler will typically auto cast the long double type back to double.

    It 'works' because the current ARM compilers do not have a larger long double type, and under the covers, both double and long double have the same representation.

    However, not all processors have this property. The Intel/AMD x86 processors typically use the internal 80-bit type for long double.

    The PowerPC compiler that I work on typically has long double being represented as a pair of double values. This is done via library routines -- there isn't hardware support directly for this paired floating point. Modern PowerPC servers (starting with power9) has native support for the IEEE 128-bit floating point that is defined in the IEEE 754R standard. One of the many tasks I have been doing over the last few years is adding the support in the GNU compiler suite to support changing the default long double format to IEEE 128-bit, and working with the GLIBC and LIBSTDC++ teams to get their support in. Most of the pieces are committed, and I have a few loose ends to get committed in January.

    Just to be clear, the following is my own opinion, and not that of my employer, my cats, or anybody else. I am speculating on why Paul made the choices he did. I tend to believe it is the right choice for the Teensy LC/3.x, but there are always side effects.

    That being said, Paul was faced with an impossible choice when the Teensy 3.0 came out. The AVR Arduino systems had a non-standard floating point implementation where float and double use the same format (IEEE 32-bit single precision). So code imported from these sources expected this behavior. But the ISO C/C++ standards demand that double has more precision than float typically has.

    The C/C++ standards specifies that if a float and double were combined, that the float would be converted to double. In addition, a floating point constant without a 'f', 'F', 'l' or 'L' sffix is automatically a double type. If you use a normal floating point constant it will mean the expression will be done in double precision.

    The Teensy 3.5/3.6 have hardware support for single precision (float) but not double precision (double). If the -fsingle-precision-constant option was not specified, it would mean you would lose a lot of the power of the machine, because it would have to do the emulation using tens, hundreds or even thousands of instructions to do basic operations.

    The Teensy 4.0/4.1 has hardware support for both single and double precision. So this option is not needed on those systems.

  21. #21
    Junior Member
    Join Date
    Dec 2020
    Posts
    6
    To Pete ,

    There is no final scetch so far , I first wanted to know if 64bit float really could be used in teensyduino , and so I know now that it can ! :-)
    I want to use it to do some astronomical calculations that I have now on 32 bit float on esp32 and see if the results are more accurate , which I expect them to be.
    If finished I surely want to share my results.

    Ronny

  22. #22
    Like I said: Saint Google doesn't know everything. Thanks for sharing your knowledge.

    I have been rechecking all the GPS code that I am building for work, I used a teensy 3.6 with the idea that its SDIO reader would help with the handling of the maps. However, it seems to me that teensy 4 or 4.1 are much better bases because of what is stated here regarding double variables.

    At last I ventured to use Haversine's formula to calculate the distance between two points stored in the GPS memory.



    Later I will share the complete project I have ventured into with teensy, let me put the pieces of the puzzle together.

    PS: You are to blame for the sleepless nights, thank you for making these fantastic MCUs come true!. I was thinking of leaving it until float ...

    Happy Holidays!. Stay safe!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •