Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 2 1 2 LastLast
Results 1 to 25 of 42

Thread: Teensy3 alternative for dtostrf() ?

  1. #1
    Senior Member
    Join Date
    Oct 2012
    Location
    Portland OR
    Posts
    676

    Teensy3 alternative for dtostrf() ?

    I have some Arduino code working now using the Adafruit 128x64 LCD using their ST7565 library, and want to port it to Teensy 3. The library required two minor fixes to compile:
    Code:
    (1) They assume they can use 'A0' as a dummy variable in their class definition, but it is already defined as a numeric constant in the T3 environment.
    (2) They #include <util/delay.h> to use _delay_ms() instead of just using delay()
    Now, to show a floating-point variable on the LCD with the ST7565 library, I need to convert the number into a string. I see that 'dtostrf()' is not available for Teensy 3. Should I instead use 'FloatToString()' http://arduino.cc/playground/Main/FloatToString or is there a better way?

  2. #2
    I wanted to ask the same thing: I have to convert a float into a string to calculate a checksum before sending it over the line, and dtostrf() works on Teensy 2, but not Teensy 3. What is the best way to convert a float to a string on a Teensy 3?

  3. #3
    Sorry for the noise: Solved it with using sprintf(), and %f format strings, and the code is easier to read too.

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,574
    Quote Originally Posted by JBeale View Post
    Code:
    (1) They assume they can use 'A0' as a dummy variable in their class definition, but it is already defined as a numeric constant in the T3 environment.
    (2) They #include <util/delay.h> to use _delay_ms() instead of just using delay()
    Are you using Teensyduino version 1.13? I recently made some changes, to support this library. Please give 1.13 a try with Arduino 1.0.4. This library is now supposed to compile and work without any editing.

  5. #5
    Junior Member
    Join Date
    Jun 2014
    Posts
    11

    more on dtostrf() and sprintf() for Teensy 2, Teensy 3

    I also ran into the issue of dtostrf() not working on T3 after porting some code I wrote while using a different Arduino board. Found that it works most of the time, which made the problem go un-noticed for a while; discovered that it seems to error only on small numbers. As you say, the sprintf() works fine for T3, as does dtostrf() for T2. It is just another thing to remember wrt portability. Here's the code I used to test and document the issue for myself.

    Code:
    // formatted float printing: Teensy 2 vs. 3
    // dtostrf() errors for small numbers ~ < 0.1 on T3
    char cbuf[20] = "";
    
    void setup() {
      while (!Serial && millis() < 5000) {}    // with timeout
      Serial.begin(9600);      
    
      float y;
      // -nn.nnnn
    
      y = -(sin(3.1416 / 4)) * 10;   // big
      dtostrf(y, 10, 5, cbuf);       //   correct on T3
      Serial.println(cbuf);       
      sprintf(cbuf, "%10.5f", y);    //   correct on T3
      Serial.println(cbuf);
    
      y = +(sin(3.1416 / 4)) / 200;  // small
      dtostrf(y, 10, 5, cbuf);       // incorrect on T3
      Serial.println(cbuf);
      sprintf(cbuf, "%10.5f", y);    //   correct on T3
      Serial.println(cbuf);
    }
    
    void loop() {
      ;
    }

  6. #6
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,700
    Indeed it isn't right, but . . .
    Perhaps showing the output and indicating where you see the problem would help focus on the nature of the problem. Especially showing the 'right' answer from the T2 as well.

    Code:
      Serial.println("-small-");
      y = -0.00123456;  // small
    My output - with the code quoted in the USB output:
    -small-
    dtostrf(y, 10, 5, cbuf); == -12346
    sprintf(cbuf, "%10.5f", y); == -0.00123
    Also the + or - before the sin is a red herring - it is wrong in both cases leaving them both the same would have been clearer.
    And the sin() itself is another - it makes the expected value unclear using this constant instead would have helped me: "y = -0.00353554; // small"

    I had to build the sketch and add to it before it became clear where the issue is. I was busy staring at the trees and missed the forest, I thought the problem was the last digit rounding, and wholly skipped the missing decimal place as not normal behavior for dtostrf() - because the actual number has a '3535' pattern, so this value is even better: "y = -0.00123456; // small"

    My not knowing dtostrf() I didn't know what to expect - you asked for 5 digits and got five? This is probably behind the error in dtostrf(), once it passes the decimal - it is out of sight and out of mind - and starts focusing on the first digit it finds since the 'leading' zeroes are boring to look at once the decimal passes because counting zeroes takes effort.

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,574
    Yup, there's probably a bug in my attempt to emulate dtostrf. Here's the code.

    https://github.com/PaulStoffregen/co...3/nonstd.c#L69

    Ultimately it uses fcvt(), which is a standard C function, but quite difficult to use correctly. I must have missed some of the cases. I've put this on my list of bugs to investigate, but realistically it's unlikely I'll be able to spend time on this until mid-November.

    If anyone has any ideas about where I went wrong in trying to emulate avr-libc's dtostrf(), please speak up!

  8. #8
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    Problay not part of the problem, but shouldnt "val" be double?

  9. #9
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,700
    Reading the manual:
    The string itself does not contain a decimal point; however, the position of the decimal point relative to the start of the string is stored in *decpt.
    I'll go glance at GitHub now

  10. #10
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,700
    I have NO IDEA of any side effects - or other cases this may affect - but on my sample this works - adding the ELSE at line 97 {"I:\Teensy165\hardware\teensy\avr\cores\teensy3\non std.c"}:

    Code:
    	if (decpt == 0 && precision > 0) {
    		*p++ = '0';
    		*p++ = '.';
    	}
    	else if (decpt < 0 && precision > 0) {
    		*p++ = '0';
    		*p++ = '.';
    		e++;
    		while ( decpt < 0 ) {
    			decpt++;
    			*p++ = '0';
    		}
    	}
    -small-
    dtostrf(y, 10, 5, cbuf); == -0.00123
    sprintf(cbuf, "%10.5f", y); == -0.00123
    Last edited by defragster; 10-28-2015 at 05:03 PM. Reason: BOLD on NEW CODE

  11. #11
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,700
    My sample test looks right - not showing anything odd (using edited nonstd.c code from post #10):

    <EDIT>: Sample INO starts with a SMALL negative Y and then loops to multiply it by 10 each loop to go from SMALL float to LARGER float.
    It then repeats with a same positive value for SMALL y
    In both cases it shows reference values from sprintf with the same %10.5f and printf for the full number value
    Code:
    char cbuf[20] = "";
    
    void setup() {
      while (!Serial && millis() < 5000) {}    // with timeout
      Serial.begin(9600);
      float y;
      Serial.println("------");
      y = -0.000012345678L;  // small
      for ( int jj = 0; jj < 2; jj++ ) {
        if ( 1 == jj ){
          y = 0.000012345678L;  // small
          Serial.println("\n=================------\n");
        }
        for ( int ii = 0; ii < 10; ii++ ) {
          Serial.print( "dtostrf(y, 10, 5, cbuf); == ");
          dtostrf(y, 10, 5, cbuf);
          Serial.println(cbuf);
          Serial.print( "sprintf(cbuf, \"%10.5f\", y); == ");
          sprintf(cbuf, "%10.5f", y);
          Serial.println(cbuf);
          Serial.print("printf(\"%f\", y);");
          Serial.printf("%f", y);
          y *= 10;
          Serial.println("\n------");
        }
      }
    }
    
    void loop() {
      ;
    }
    ------
    dtostrf(y, 10, 5, cbuf); == -0.00001
    sprintf(cbuf, "%10.5f", y); == -0.00001
    printf("%f", y);-0.000012
    ------
    dtostrf(y, 10, 5, cbuf); == -0.00012
    sprintf(cbuf, "%10.5f", y); == -0.00012
    printf("%f", y);-0.000123
    ------
    dtostrf(y, 10, 5, cbuf); == -0.00123
    sprintf(cbuf, "%10.5f", y); == -0.00123
    printf("%f", y);-0.001235
    ------
    dtostrf(y, 10, 5, cbuf); == -0.01234
    sprintf(cbuf, "%10.5f", y); == -0.01235
    printf("%f", y);-0.012346
    ------
    dtostrf(y, 10, 5, cbuf); == -0.12346
    sprintf(cbuf, "%10.5f", y); == -0.12346
    printf("%f", y);-0.123457
    ------
    dtostrf(y, 10, 5, cbuf); == -1.23457
    sprintf(cbuf, "%10.5f", y); == -1.23457
    printf("%f", y);-1.234568
    ------
    dtostrf(y, 10, 5, cbuf); == -12.34568
    sprintf(cbuf, "%10.5f", y); == -12.34568
    printf("%f", y);-12.345678
    ------
    dtostrf(y, 10, 5, cbuf); == -123.45679
    sprintf(cbuf, "%10.5f", y); == -123.45679
    printf("%f", y);-123.456787
    ------
    dtostrf(y, 10, 5, cbuf); == -1234.56787
    sprintf(cbuf, "%10.5f", y); == -1234.56787
    printf("%f", y);-1234.567871
    ------
    dtostrf(y, 10, 5, cbuf); == -12345.67871
    sprintf(cbuf, "%10.5f", y); == -12345.67871
    printf("%f", y);-12345.678711
    ------

    =================------

    dtostrf(y, 10, 5, cbuf); == 0.00001
    sprintf(cbuf, "%10.5f", y); == 0.00001
    printf("%f", y);0.000012
    ------
    dtostrf(y, 10, 5, cbuf); == 0.00012
    sprintf(cbuf, "%10.5f", y); == 0.00012
    printf("%f", y);0.000123
    ------
    dtostrf(y, 10, 5, cbuf); == 0.00123
    sprintf(cbuf, "%10.5f", y); == 0.00123
    printf("%f", y);0.001235
    ------
    dtostrf(y, 10, 5, cbuf); == 0.01234
    sprintf(cbuf, "%10.5f", y); == 0.01235
    printf("%f", y);0.012346
    ------
    dtostrf(y, 10, 5, cbuf); == 0.12346
    sprintf(cbuf, "%10.5f", y); == 0.12346
    printf("%f", y);0.123457
    ------
    dtostrf(y, 10, 5, cbuf); == 1.23457
    sprintf(cbuf, "%10.5f", y); == 1.23457
    printf("%f", y);1.234568
    ------
    dtostrf(y, 10, 5, cbuf); == 12.34568
    sprintf(cbuf, "%10.5f", y); == 12.34568
    printf("%f", y);12.345678
    ------
    dtostrf(y, 10, 5, cbuf); == 123.45679
    sprintf(cbuf, "%10.5f", y); == 123.45679
    printf("%f", y);123.456787
    ------
    dtostrf(y, 10, 5, cbuf); == 1234.56787
    sprintf(cbuf, "%10.5f", y); == 1234.56787
    printf("%f", y);1234.567871
    ------
    dtostrf(y, 10, 5, cbuf); == 12345.67871
    sprintf(cbuf, "%10.5f", y); == 12345.67871
    printf("%f", y);12345.678711
    ------
    Last edited by defragster; 10-28-2015 at 05:22 PM. Reason: opps : removed extraneous 'code' printf from output

  12. #12
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,700
    Issued PULL request. GitHub was harder to navigate than editing the code was - but maybe I'm learning.

    https://github.com/PaulStoffregen/cores/pull/89

  13. #13
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    Quote Originally Posted by defragster View Post
    I have NO IDEA of any side effects - or other cases this may affect - but on my sample this works - adding the ELSE at line 97 {"I:\Teensy165\hardware\teensy\avr\cores\teensy3\non std.c"}:

    Code:
        if (decpt == 0 && precision > 0) {
            *p++ = '0';
            *p++ = '.';
        }
        else if (decpt < 0 && precision > 0) {
            *p++ = '0';
            *p++ = '.';
            e++;
            while ( decpt < 0 ) {
                decpt++;
                *p++ = '0';
            }
        }
    Seems to work!:

    You can omit the previous "if":
    just delete it (incl. else) and write if (decpt <= 0 && precision > 0) { instead

    so the complete function is now:

    Code:
    char * dtostrf(double val, int width, unsigned int precision, char *buf)
    {
        int decpt, sign, reqd, pad;
        const char *s, *e;
        char *p;
    
        s = fcvt(val, precision, &decpt, &sign);
        if (precision == 0 && decpt == 0) {
            s = (*s < '5') ? "0" : "1";
            reqd = 1;
        } else {
            reqd = strlen(s);
            if (reqd > decpt) reqd++;
            if (decpt == 0) reqd++;
        }
        if (sign) reqd++;
        p = buf;
        e = p + reqd;
        pad = width - reqd;
        if (pad > 0) {
            e += pad;
            while (pad-- > 0) *p++ = ' ';
        }
        if (sign) *p++ = '-';
        if (decpt <= 0 && precision > 0) {
            *p++ = '0';
            *p++ = '.';
            e++;
            while ( decpt < 0 ) {
                decpt++;
                *p++ = '0';
            }
        }    
        while (p < e) {
            *p++ = *s++;
            if (p == e) break;
            if (--decpt == 0) *p++ = '.';
        }
        if (width < 0) {
            pad = (reqd + width) * -1;
            while (pad-- > 0) *p++ = ' ';
        }
        *p = 0;
    
        //char format[20];
        //sprintf(format, "%%%d.%df", width, precision);
        //sprintf(buf, format, val);
        return buf;
    }
    of course, avr_functions.h needs to be edited:
    char * dtostrf(double val, int width, unsigned int precision, char *buf);
    (line101)
    Last edited by Frank B; 10-28-2015 at 05:23 PM.

  14. #14
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,700
    @Frank: I thought about that clause being suspect <edit: subject to removal or inclusion> with my change - but I modified the value of 'e' and didn't want to actually look any further in the code for side effects. Without that change it came up one byte short on output - with it in any other case it might start adding digits.
    Last edited by defragster; 10-28-2015 at 11:23 PM.

  15. #15
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,700
    Cool! The PULL is done!

  16. #16
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    cool :O)

    but the "float val" parameter still seems not to be correct for me .

    i just found the declaration of avr-libc:
    http://www.nongnu.org/avr-libc/user-...168b3ce8771d42
    search for dtostrf

    char* dtostrf ( double __val,
    signed char __width,
    unsigned char __prec,
    char * __s
    )
    The dtostrf() function converts the double value passed in val

  17. #17
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,256
    Yes, the function takes a double argument. However, since C++ requires prototypes in scope, the compiler will automatically convert a float passed to dtostrf to double.

    Even in the case of ancient C without prototypes, it would have worked, since if you don't have a prototype in scope, the compiler will create a prototype for you, and char/short arguments will be converted to int, and float arguments are converted to double.

  18. #18
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    Quote Originally Posted by MichaelMeissner View Post
    Yes, the function takes a double argument. However, since C++ requires prototypes in scope, the compiler will automatically convert a float passed to dtostrf to double.

    Even in the case of ancient C without prototypes, it would have worked, since if you don't have a prototype in scope, the compiler will create a prototype for you, and char/short arguments will be converted to int, and float arguments are converted to double.
    @Michael:
    I don't understand.. sometimes my english is not good enough..

    In avr_functions.h (teensy3-code): char * dtostrf(float val, int width, unsigned int precision, char *buf);
    The function in nonstd.c (teensy3-code) is the same:
    char * dtostrf(float val, int width, unsigned int precision, char *buf) {...}

    So, that float is correct, and instead float the compiler uses double?

    I thought, if you call this function with a double, tha value is converted to float, and then later in the function the float value is again converted to double for fcvt()
    So i am wrong ? Changing it to char * dtostrf(double val, is not needed ??

    Why is in the AVR-LIBC (which is NOT usd here, or is it??) a different prototype?
    Last edited by Frank B; 10-28-2015 at 08:05 PM.

  19. #19
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,256
    The documentation that I've seen has a double as the first argument.

    Note, in the AVR system, double is not 64-bits, but it is 32-bits, just like float (presumably to save space). AVR programmers tend to be somewhat lax about the difference between float and double. So, I could imagine that in the AVR specific section, they use float instead of double.

  20. #20
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    I'm still confused!
    Was i wrong with my previous posting or not ? This is no rhetorical question, really, i have problems to understand(=translating to german) your post #17.

    The name is DOUBLEtostrf() . Does it convert DOUBLE or FLOAT (i'm speaking of pauls version) ??
    Yes i know that double is the same as float on avr. But wer're talking about teensy3.

  21. #21
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    This is the Arduino-code for AVR_SAM, which is 32BIT too:
    https://github.com/arduino/ArduinoCo.../avr/dtostrf.c

    Code:
    char *dtostrf (double val, signed char width, unsigned char prec, char *sout) {
      char fmt[20];
      sprintf(fmt, "%%%d.%df", width, prec);
      sprintf(sout, fmt, val);
      return sout;
    }

  22. #22
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,574
    Quote Originally Posted by MichaelMeissner View Post
    AVR programmers tend to be somewhat lax about the difference between float and double.
    Yes, exactly!

    I think it's important to keep in mind dtostrf() is AVR specific. While it could be useful on other platforms, the fact is it only exists on AVR. The avr-libc developers made it up. We're trying to emulate it on ARM only because some authors of Arduino sketches and libraries have used this AVR-only function.

    Since AVR only has single precision, I defined this function with float instead of double. The idea was to get as close as reasonably possible to what AVR actually provides. Ideally, I'd like to replace fcvt() with a single precision function. But not enough to write one myself!

  23. #23
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    just edit fcvt() to fcvtf()

    ok, i understand the compatibility-reason

    so, i'm right that the existing dtostrf really uses float and not double as Michael said ?
    Or did i understand him wrong ?
    Last edited by Frank B; 10-28-2015 at 09:57 PM.

  24. #24
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    just to make it more clear: the fcvtf() is already existing.
    (This evening i'm a bit afraid that everybody is understanding me wrong.... perhaps i should go back to German forums..)

  25. #25
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    I'm now pause and hear some RAMSTEIN CDs

Posting Permissions

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