Unexpected result using division with signed integers

Asked by Ian Duffy

The following code produces surprising results
{
 signed int a = -9;
 unsigned int b = 3;
 signed int c = a / b;
 signed short d = a / b;
}

Expected c = -3, d = -3
Get c = 1431655762, d = 21842

Clearly the compiler is casting a to an unsigned int before doing the division and then casting the result back.

However, regardless of the value of a or b, the result can always fit in a signed int with no overflow so there should be no problem always producing the correct answer.

In pseudo code I'd expect the following to happen:

signed int divide(signed int a, unsigned int b)
{
   if b > abs(a) return 0;
   return a / (signed int)b;
}

I don't know if this is a bug or a subtle trap in the C spec ready to catch the unwary.

For the record I'm using
arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -g ...

Question information

Language:
English Edit question
Status:
Solved
For:
GNU Arm Embedded Toolchain Edit question
Assignee:
No assignee Edit question
Solved by:
Joey Ye
Solved:
Last query:
Last reply:
Revision history for this message
Joey Ye (jinyun-ye) said :
#1

Ian, I couldn't reproduce it. This is such a simply case that I doubt
compiler can do it wrong.

Can you please post the disassembled function for me to diagnose?

Joey
On Sep 6, 2013 7:53 PM, "Ian Duffy" <email address hidden>
wrote:

> New question #235262 on GCC ARM Embedded:
> https://answers.launchpad.net/gcc-arm-embedded/+question/235262
>
> The following code produces surprising results
> {
> signed int a = -9;
> unsigned int b = 3;
> signed int c = a / b;
> signed short d = a / b;
> }
>
> Expected c = -3, d = -3
> Get c = 1431655762, d = 21842
>
> Clearly the compiler is casting a to an unsigned int before doing the
> division and then casting the result back.
>
> However, regardless of the value of a or b, the result can always fit in a
> signed int with no overflow so there should be no problem always producing
> the correct answer.
>
> In pseudo code I'd expect the following to happen:
>
> signed int divide(signed int a, unsigned int b)
> {
> if b > abs(a) return 0;
> return a / (signed int)b;
> }
>
> I don't know if this is a bug or a subtle trap in the C spec ready to
> catch the unwary.
>
> For the record I'm using
> arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -g ...
>
>
>
>
>
>
>
> --
> You received this question notification because you are an answer
> contact for GCC ARM Embedded.
>

Revision history for this message
Ian Duffy (iduffy) said :
#2

My test function looks like this:

signed int test(void)
{
 signed int a = -9;
 unsigned int b = 3;
 return a / b;
}

The assembler generated using -S switch looks lke this:

test:
.LFB110:
 .loc 1 46 0
 .cfi_startproc
 @ args = 0, pretend = 0, frame = 8
 @ frame_needed = 1, uses_anonymous_args = 0
 @ link register save eliminated.
 push {r7}
.LCFI0:
 .cfi_def_cfa_offset 4
 .cfi_offset 7, -4
 sub sp, sp, #12
.LCFI1:
 .cfi_def_cfa_offset 16
 add r7, sp, #0
.LCFI2:
 .cfi_def_cfa_register 7
 .loc 1 47 0
 mvn r3, #8
 str r3, [r7, #4]
 .loc 1 48 0
 mov r3, #3
 str r3, [r7, #0]
 .loc 1 49 0
 ldr r2, [r7, #4]
 ldr r3, [r7, #0]
 udiv r3, r2, r3
 .loc 1 50 0
 mov r0, r3
 add r7, r7, #12
 mov sp, r7
 pop {r7}
 bx lr
 .cfi_endproc

A bit more context ..
I'm compiling using CooCox IDE on Windows 8.
Tool chain path is "C:\Program Files (x86)\GNU Tools ARM Embedded\4.7 2013q1\bin"
My target is a STM32F407VG

I didn't include the full command in my earlier post as I thought some of the options were irrelevant, but just in case I'm mistaken here it is (apart from the include paths and filenames)
 arm-none-eabi-gcc -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -Wall -ffunction-sections -g -O0 -std=c99 -S -c -DSTM32F407VG -DSTM32F4XX -DUSE_STDPERIPH_DRIVER -DUSE_USB_OTG_FS -D__FPU_USED

The project I'm currently working on is quite large with lots of libraries. I'll try to create a bare-bones project with as little functionality as possible to reproduce the same behaviour.

Revision history for this message
Ian Duffy (iduffy) said :
#3

Just to confirm that it also happens in a brand new project with just start up code and this function.

Revision history for this message
Best Joey Ye (jinyun-ye) said :
#4

Now I can reproduce it. It is correct and expected behavior.

Here you are mixing signed and unsigned integer operation. Acording to Ansi- specification http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf, section 6.3.1.8:

"Otherwise, if the operand that has unsigned integer type has rank greater or
equal to the rank of the type of the other operand, then the operand with
signed integer type is converted to the type of the operand with unsigned
integer type."

In your case signed A is converted to unsigned integer, which is 4294967287, divided by 3 is 1431655762.

To make it work as you expected, please cast b to signed integer:
return a/(signed int)b;

Thank again bring up this discussion, from which I learnt something too.

- Joey

Revision history for this message
Ian Duffy (iduffy) said :
#5

Thanks. I thought it would be surprising for the compiler to be wrong too :-)