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
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.
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
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
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.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!
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
And using another MSP430 is no option? If you use legacy COFF, is the remaining free memory enough for you?
You need to use the "f" floating point conversion conversion specifier and give the precision required. e.g. try "%.0f".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?
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.
Can you explain in more detail what output format you require?GURU MOORTHY said:But I don't need the terminating zeros which i get in %f
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
I think you might have to post-process the sprintf output to remove trailing zeros. See Avoid trailing zeroes in printf()GURU MOORTHY said:I need 8 significant numbers max and 6 radix numbers are sufficient. But I don't want trailing zeros.
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.
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:f. m. said:That is not a question of the ABI, but of the libraries used.
Table 5.1. MSP430 C/C++ COFF ABI Data Types
Range | ||||
---|---|---|---|---|
Type | Size | Representation | Minimum | Maximum |
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 | |||||
---|---|---|---|---|---|
Type | Size | Alignment | Representation | Minimum | Maximum |
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.
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.
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 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.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.
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
**Attention** This is a public forum