Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
6 years, 6 months ago.
Possible bug with printf and large unsigned integers
I have noticed that when printing (over the serial port back to a PC) large 32-bit unsigned integers, printf doesn't seem to handle large values above the signed integer maximum correctly. Or am I missing something?
Example code:
#include "mbed.h" Serial usb(USBTX, USBRX); int main() { unsigned int t = 0x7FFFFFFC; while(1) { wait(0.5); usb.printf("%X %d %f %f\n", t, t, (float)t, (double)t); t++; } }
Example code results:
7FFFFFFC 2147483644 2147483648.000000 2147483644.000000
7FFFFFFD 2147483645 2147483648.000000 2147483645.000000
7FFFFFFE 2147483646 2147483648.000000 2147483646.000000
7FFFFFFF 2147483647 2147483648.000000 2147483647.000000
80000000 -2147483648 2147483648.000000 2147483648.000000
80000001 -2147483647 2147483648.000000 2147483649.000000
80000002 -2147483646 2147483648.000000 2147483650.000000
80000003 -2147483645 2147483648.000000 2147483651.000000
NB: The reason the float format value doesn't appear to change is due to it not having sufficient resolution to show all 32 bits of the integer, consequently the value displayed only changes every 256 counts.
Regards, Mike.
1 Answer
6 years, 6 months ago.
Hello Mike,
Rather than using d
, try to use u
as specifier to print unsigned integers:
usb.printf("%X %u %f %f\n", t, t, (float)t, (double)t);
Also in the interests of of code portability if you need 32 bits you should use long unsigned int or uint32_t rather than unsigned int which on some platforms will only be 16 bits. Similarly you should technically also use %lu in the printf (and %lx for hex output and %lf for the double)
posted by 24 May 2018Thanks Zoltan, in my ignorance I didn't know about the %u format specifier.
I went on to check printf with unsigned long long (uint64) and discovered the following behaviour... - %X works upto 2^32, anything higher wraps around to zero. - %d seems to output only the value from the top 32 bits of the 64. - %u does the same as %X except with decimal numbers. - (FLOAT)uint64 returns zero. - (DOUBLE)uint64 behaves well (albeit with limited resolution).
Regards, Mike.
posted by 24 May 2018The ARM chips are 32 bits which means that integer operations will generally be 32 bits unless otherwise specified meaning %d will work with up to 32 bits.
However the c standard requires that integers be at least 16 bits with no upper limit. Exact implementation is up to the compiler. Generally int will be implemented as the native size on that processor since that will give the best performance so it could be 16, 32 or 64 bits.
So code that uses an int or int format specifiers in a printf for 32 bit values will work fine on a 32 bit ARM chip but then cause errors if compiled for some other devices. On a 64 bit processor %d would probably work for a 64 bit value.
While you may not have any plans to ever move this particular code to a different processor it's a good habit to always explicitly state the size of an integer when it matters. e.g. if you want a counter that rolls over at 2^16 then use a uint16_t, if you need 32 bits use a uint32_t. If you need a variable to count through the indexes of an array the for speed use int unless it could potentially have more than 16,000 entries.
posted by 25 May 2018