This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

sprintf function in ccs

Other Parts Discussed in Thread: MSP430G2553

Hello,

I am using CCSV5. I have a problem. The sprintf( ) function does not work for float or "%f". I am using MSP430G2553 MCU.

Please help.

Regards

  • By default, the sprintf function supports only a minimum set of features and converting floats is, of course, not included. The reason for this is the larger code size that more support requires which is not suitable for smaller processors. But you can change the settings for the sprintf support in the preferences. For floats you need the full support.

    Dennis

  • please guide how to do that

    Regards
  • i am using msp430g2553. Will it be possible to use sprintf?

    Regards
  • Hi Guru!

    GURU MOORTHY said:
    please guide how to do that

    To change the settings, right-click on your project in the Project Explorer and select Properties:

    You can also simply press ALT+ENTER. Then this window opens:

    Go to (CCS) Build -> MSP430 Compiler -> Advanced Options -> Library Function Assumptions and change the Level of printf support to full for using floating point values.

    GURU MOORTHY said:
    i am using msp430g2553. Will it be possible to use sprintf?

    No, I would not use sprintf with floating point values on that small processor. I made an example for you - this is the code:

    #include <msp430g2553.h> 
    #include <stdint.h>
    #include <stdio.h>
    
    #define VOLTAGE_V 1.234
    
    char    buffer[50];
    uint8_t nr_of_chars;
    
    
    void main( void )
    {
      WDTCTL = WDTPW | WDTHOLD;	// Stop watchdog timer
    	
      nr_of_chars = sprintf( buffer, "The sensor voltage is %.3f V", VOLTAGE_V );
    
      while( 1 );
    }

    This program writes the given text and the converted floating point value with three decimal places to the buffer, adds the string terminator \0 and it stores the number of the written characters (without the termination character) to the variable.

    Content of buffer:

    The sensor voltage is 1.234 V\0

    Content of nr_of_chars:

    29

    As you can see, I pass the floating point value VOLTAGE_V (1.234) to the sprintf function. Full support for printf is enabled. The problem:

    As you can see, the code size is 15,746 bytes. The MSP430G2553 has 16k of flash. Your MSP430 is already full!

    Now consider if you really need to use floats. In the given example you could use mV instead of V and set the printf support to minimal. Now without using floating point - imagine you had multiplied your floating point value of 1.234 by 1000, resulting in 1234 integer only. This is the new code:

    #include <msp430g2553.h> 
    #include <stdint.h>
    #include <stdio.h>
    
    #define VOLTAGE_MV 1234
    
    char    buffer[50];
    uint8_t nr_of_chars;
    
    
    void main( void )
    {
      WDTCTL = WDTPW | WDTHOLD;	// Stop watchdog timer
    	
      nr_of_chars = sprintf( buffer, "The sensor voltage is %d mV", VOLTAGE_MV );
    
      while( 1 );
    }

    Content of buffer:

    The sensor voltage is 1234 mV\0

    Content of nr_of_chars:

    29

    Now I use the unsigned integer and %d instead of %.3f for the sprintf function. The result in code size:

    This is a difference, isn't it?

    Of course you could now think if you need sprintf at all because you can also implement this functionality on your own, at least for this simple example, but this depends on your program. To convert 1234 to ASCII chars, you can do the following:

    value = 1234;
    
    thousands  = (value / 1000) + 48;
    value     %= 1000;
    hundreds   = (value / 100) + 48;
    value     %= 100;
    tens       = (value / 10) + 48;
    ones       = (value % 10) + 48;

    The +48 is the conversion from decimal to ASCII.

    Dennis

  • Dennis Eichmann said:
    As you can see, I pass the floating point value VOLTAGE_V (1.234) to the sprintf function. Full support for printf is enabled. The problem:

    As you can see, the code size is 15,746 bytes. The MSP430G2553 has 16k of flash. Your MSP430 is already full!

    That code size is with the Output Format of the compiler set to "eabi (ELF)". The EABI output format has the type single as 32-bits and double as 64-bits, and so the floating point support for the sprintf function has to support 64-bit floating point calculations.

    If the Output Format is changed to "legacy COFF" then the code size taken by the example reduces to 10,030 bytes. This is because with the COFF output format the double type is 32-bits, meaning the floating point support for the sprintf function only has to support 32-bit floating point calculations.

    Agree that on memory constrained devices such as the MSP430G2553 use of floating point calculations is not recommended, due to the size of run time support code required for the software floating-point operations.

  • Thanks for the additional input and testing with legacy COFF.

    Chester Gillon said:
    the code size taken by the example reduces to 10,030 bytes

    Still quite large code size for this little task.

    Dennis

  • hi,
    I need to convert float to string up-to 8 decimal places which I can't avoid. But I am not finding a way to do it. Any help on this matter is appreciated

    Regards
  • And using another MSP430 is no option? If you use legacy COFF, is the remaining free memory enough for you?

  • Hello,
    Yeah I got it working by using legacy coff.
    But, I need one more guidance. I am using %g to display. But as the number goes above 6 digits it shifts into exponential format like 1.234567e+6 instead of 12345678. I need it in latter format (12345678)only. How can I do that? Please help.

    Regards
  • GURU MOORTHY said:
    . I am using %g to display. But as the number goes above 6 digits it shifts into exponential format like 1.234567e+6 instead of 12345678. I need it in latter format (12345678)only. How can I do that?

    You need to use the "f" floating point conversion conversion specifier and give the precision required. e.g. try "%.0f".

    See the C library documentation for the sprintf format string options.

  • Chester Gillon said:

    You need to use the "f" floating point conversion conversion specifier and give the precision required. e.g. try "%.0f".

    See the C library documentation for the sprintf format string options.

    Hello,
    But I don't need the terminating zeros which i get in %f
    Regards
  • GURU MOORTHY said:
    But I don't need the terminating zeros which i get in %f

    Can you explain in more detail what output format you require?

  • Hello,
    I need 8 significant numbers max and 6 radix numbers are sufficient. But I don't want trailing zeros.

    Regards
  • Chester Gillon said:

    Can you explain in more detail what output format you require?

    Hi,

    the format would be like that of a simple 8-digit calculator

    Regards

  • GURU MOORTHY said:
    I need 8 significant numbers max and 6 radix numbers are sufficient. But I don't want trailing zeros.

    I think you might have to post-process the sprintf output to remove trailing zeros. See Avoid trailing zeroes in printf()

  • Hello,
    I am very much grateful to you all for helping me in this issue. I thank you all for guiding me. I some how sorted it out and attained my requirement approximately. I couldn't do it 100% due to code size limitation and printf format limitations.

    Regards.
  • Hello,
    I am using sprintf function to write a double number to char array. But numbers like 8999999 get rounded to 90000000 or 7999996 to 90000000. Why does that happen and how do i solve it. I use %.9g in sprintf function.
  • Guru,

    could you post an example of what input and what output you want to have?

    Dennis
  • Looks like it's actually single precision (float). Is the variable you want to print a float ? Casting to double does not increase precision.
    I know of some toolchains that allow for a "treat all double as float" setting. That is good for porting code from the PC world while limiting resource usage on smaller MCUs. I suggest to check this.
  • Hello,
    How do I check whether it is single precision or double precision. I am using COFF format. So I know it is 32 bit and I went for it because Eabi did not support some operation due to smaller MCU.
  • How do I check whether it is single precision or double precision.

    In the source code. In one of your last posts you wrote :

    I am using sprintf function to write a double number to char array.

    That implies "double precision" variables, but the reported accuracy not.

    If you are unsure about some 'hidden' option to treat doubles as as floats, either check sizes in the map file, or check the return of a sizeof() during runtime with a debugger.

    So I know it is 32 bit and I went for it because Eabi did not support some operation due to smaller MCU.

    That is not a question of the ABI, but of the libraries used. Single precision (float) has - obviously - less precision (i.e. less digits) than double, but requires less resources for emulation. ABI/EABI is about function calls, stack frames and parameter passing, not about data types. Cortex M has an ABI option to pass floating point parameters in FPU registers (-mfloat-abi=hard), but this requires a FPU at runtime.

  • f. m. said:
    That is not a question of the ABI, but of the libraries used.

    Actually, as of the MSP430 compiler version 4.4 changing the ABI does change the size of double as shown by the following tables from the MSP430 Optimizing C/C++ Compiler User's Guide:

    Table 5.1. MSP430 C/C++ COFF ABI Data Types

       Range
    TypeSizeRepresentationMinimumMaximum
    signed char 8 bits Binary -128 127
    char 8 bits ASCII 0 or -128 [a] 255 or 127 [b]
    unsigned char, bool, _Bool 8 bits Binary 0 255
    short, signed short 16 bits 2s complement -32 768 32 767
    unsigned short 16 bits Binary 0 65 535
    int, signed int 16 bits 2s complement -32 768 32 767
    unsigned int, wchar_t 16 bits Binary 0 65 535
    long, signed long 32 bits 2s complement -2 147 483 648 2 147 483 647
    unsigned long 32 bits Binary 0 4 294 967 295
    long long, signed long long 64 bits 2s complement -9 223 372 036 854 775 808 9 223 372 036 854 775 807
    unsigned long long 64 bits Binary 0 18 446 744 073 709 551 615
    enum [c] 16 bits 2s complement -32 768 32 767
    float 32 bits IEEE 32-bit 1.175 495e-38[d] 3.40 282 35e+38
    double 32 bits IEEE 32-bit 1.175 495e-38[e] 3.40 282 35e+38
    long double 32 bits IEEE 32-bit 1.175 495e-38[f] 3.40 282 35e+38
    function and data pointers (see Table 5.3)        

    Table 5.2. MSP430 C/C++ EABI Data Types

        Range
    TypeSizeAlignmentRepresentationMinimumMaximum
    signed char 8 bits 8 Binary -128 127
    char 8 bits 8 ASCII 0 or -128 [a] 255 or 127 [b]
    unsigned char 8 bits 8 Binary 0 255
    bool (C99) 8 bits 8 Binary 0 (false) 1 (true)
    _Bool (C99) 8 bits 8 Binary 0 (false) 1 (true)
    bool (C++) 8 bits 8 Binary 0 (false) 1 (true)
    short, signed short 16 bits 16 2s complement -32 768 32 767
    unsigned short 16 bits 16 Binary 0 65 535
    int, signed int 16 bits 16 2s complement -32 768 32 767
    unsigned int 16 bits 16 Binary 0 65 535
    long, signed long 32 bits 16 2s complement -2 147 483 648 2 147 483 647
    unsigned long 32 bits 16 Binary 0 4 294 967 295
    long long, signed long long 64 bits 16 2s complement -9 223 372 036 854 775 808 9 223 372 036 854 775 807
    unsigned long long 64 bits 16 Binary 0 18 446 744 073 709 551 615
    enum varies [c] 16 2s complement varies varies
    float 32 bits 16 IEEE 32-bit 1.175 494e-38[d] 3.40 282 346e+38
    double 64 bits 16 IEEE 64-bit 2.22 507 385e-308[e] 1.79 769 313e+308
    long double 64 bits 16 IEEE 64-bit 2.22 507 385e-308[f] 1.79 769 313e+308
    function and data pointers varies (see Table 5.3) 16      

    i.e. using the COFF ABI means double is 32-bits and thus single precision.

  • Hi,
    I need 8 significant digits and 8 decimal digits. Can I get it in single precision with good accuracy, or is it not possible?
  • Hi,
    If I use Eabi, my code size goes beyond the available space. If I use COFF, I'll get wrong values. I am typically in a deadlock. Finding it difficult to come out of this deadlock.
  • Interesting to note - coming from the Cortex M world, this is somehow surprising. I know COFF as executable/library format. Attaching such data type restrictions via an ABI to an executable format seems a strange idea to me. No wonder it is replaced ...

    Something learned again.

  • As I have learned now, the "treat double as float" is implied in the COFF ABI. So at least this assumption was correct.

    If I use COFF, I'll get wrong values.

    That depends on your definition of "wrong". Even "double" or "long double" have limited precision and cannot represent every (mathematically) possible number. The question is - does the limited precision of float break your application ?

    My suggestion is to test a float and a double implementation of your algorithm/calculation (perhaps on another platform like a PC, for instance) and check if you can live with the loss of precision.

  • Hi,
    It is difficult to live with loss of precision because the application that I am working on is a 8 digit simple calculator. As I give inputs, if my device rounds it off, then it would be a stupid calculator to make use of.

    Regards
  • It is difficult to live with loss of precision because the application that I am working on is a 8 digit simple calculator.

    This suggests performance is not a primary issue (i.e. not a large amount of complex calculation in a few microseconds).

    Perhaps one of the arbitrary-precision libraries there is of interest - they are not based on IEEE754 floating point. You could port the parts of the functionality you need.

    For hardcore coders, it is also possible to implement arbitrary-precision algorithms yourself ...

  • With floating point, there will always be some approximation. It is side-effect of going back and forth from base-10 to base-2.
    I think that your floating point approach is not in the spirit of a MSP430 assignment. Perhaps you try a different approach. Scale everthing up and stay with integer math. For example, a simple 8 digit calculator only handles this range:
    0.0000001 to 99999999
    Scale by 1000000 to give a range of
    1 to 99999999000000
    This quite easily fits into the 64 bit unsigned long long type (0-18446744073709551615). Do your math operations (+,-,x./) in integer. Check for underflow and oveflow after each operation.
    The key is to read in the number and print out the number without going through floating point.
  • GURU MOORTHY said:
    If I use Eabi, my code size goes beyond the available space. If I use COFF, I'll get wrong values. I am typically in a deadlock.

    With the MSP430 compiler run time library, to get floating point support in sprintf() requires the --printf_support to be set to "full". This links in code to support all the sprintf format specifiers, some of which may not be required by your application but which increase the code size.

    In the CCS installation the source code for the core sprintf/printf/fprintf functionality is in ccsv6/tools/compiler/ti-cgt-msp430_<compiler_version>/lib/src/_printfi.c. You could try copying only the required formatting code from _printfi.c into your project, and see if that gives sufficient code space to be able to use double-precision with EABI.

  • >I need 8 significant digits and 8 decimal digits
    99'999'999 = 0x05F5_E0FF so that part is a long, so I would do singed fixed point long long.
    maybe have to roll your own fixed point as I don't see it as a C standard?
    32bit+32bit, eg 0x0000_0001_0000_0000 = 1.0

    maybe need a union when you just want to add integral numbers  to the integral part.
    you would add the decimal point when you print it out, fixedpoint.integral + "." + fixedpoint.fractional;

    typedef union fixedpointTag{
      long long LLong;
      struct{
        unsigned long fractional; // little-endian
        long integral;
      };
    }fixedpoint;                  // not tested