简体   繁体   中英

AVR C hex and dec multiplication not as expected

I just discovered a bug in my code when multiplying hex integers (ex. 0xFFFF) with dec integers (ex. 2). This is the code where the problem occurs:

print_int_new_line(0xFFFF*2);
print_int_new_line(65535*2);

Executing this code gives me the following result:

65534
131070

This is relevant UART code:

void print_int_new_line(uint64_t data) {
    print_int(data);

    print_new_line();
}

void print_int(uint64_t data) {
    char data_buffer[(int)(log(data)+2)]; // size of the number (log(number)+1) + 1 for the /0

    ultoa(data, data_buffer, 10); // convert the int to string, base 10

    // print the newly created string
    print_string(data_buffer);
}

void print_string(char * data) {

    // transmit the data char by char
    for(; *data != '\0'; data++){
        USART_transmit(data);
    }
}

void USART_transmit(const char * data){
    /* Wait for empty transmit buffer */
    while ( !( UCSR0A & (1<<UDRE0)) )
    ;

    /* Put data into buffer, sends the data */
    UDR0 = *data;
}

Some info about my setup:

MCU: ATmega2560 Board: Arduino Mega2560 UART baudrate: 38400 IDE: Atmel Studio 7.0.4.1417

Using the AVR toolchain.

I read on this stackoverflow page that multiplication is possible between hex and dec ints. Also, testing this in an online c compiler gives the correct output.

Can anyone give me an explanation?

This behavior is due to differences in handling decimal and hexadecimal integer constants.

For both 0xFFFF and 65535 , the compiler will first try to convert the value to an int . But since the platform has a 16-bit int type where INT_MAX is 32767 , that conversion cannot be performed.

The key difference is the next step. For the hexadecimal constant 0xFFFF , the compiler will try to convert it to an unsigned int , which it does to the equivalent of (unsigned int)65535 . But, for the decimal constants, conversions to unsigned types are not attempted. The next conversion attempt is to long int . This succeeds and is equivalent to (long int)65535 .

So the calls to print_int_new_line are equivalent to:

print_int_new_line((unsigned int)65535*2);
print_int_new_line((long int)65535*2);

And when 2 is promoted to do the multiplication:

print_int_new_line((unsigned int)65535*(unsigned int)2);
print_int_new_line((long int)65535*(long int)2);

The unsigned int result of the first multiplication is too small to hold the full result, so it is truncated to 65534 . The long int can hold the result, so it produces the correct answer of 131070 .

You can force the hexadecimal constant to use a long int by appending an L (ie 0xFFFFL ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM