3

I'm facing a weird issue when testing my software on arm64.

I've written a bit of code to reproduce the issue :

char c;
int sum = 0;
for (int i = 0; i <= 255; i++)
{
   c = i;
   int a = c * 10;
   sum += a;
   std::cout << a << std::endl;
}

When I run it on Windows (built with Visual Studio 2017) or Ubuntu x64 (gcc 9.3.0-17) I have the following results :

0
10
...
1260
1270
-1280
-1270
-20
-10
sum=-1280

if I run the same code on Ubuntu arm64 (gcc 9.3.0-17), I have different results :

0
10
...
1260
1270
1280
1290
...
2540
2550
sum=326400

I don't know if there is some extra optimization in gcc on arm64 (using -O3) or if there is some issue I don't see ? Any idea on how I can solve this issue?

Gerald
  • 690
  • 4
  • 13
  • 3
    Perhaps you should check whether `char` is signed or unsigned type on arm platform instead of assuming it. Note that `char` is a distinct type to `signed char` and `unsigned char`. – user7860670 Jul 20 '21 at 16:27
  • 2
    It's not the hardware; it's the compiler that decides signedness and overflow behavior. – Pete Becker Jul 20 '21 at 16:29
  • @alfC I think the OP has *not* converted to `int` first, so as to reproduce the issue. Their 'real world' code may be rather more subtle/complex. – Adrian Mole Jul 20 '21 at 16:49
  • @alfC Also, in your suggestion, the `static_cast(c)` will be too late - the assignment and (likely) overflow will already have happened in the previous statement. – Adrian Mole Jul 20 '21 at 16:57
  • 2
    `char` can be signed or unsigned, and on ARM it's usually unsigned by default whereas most architectures use signed char by default: [Why unsigned types are more efficient in arm cpu?](https://stackoverflow.com/q/3093669/995714), [Is char signed or unsigned by default?](https://stackoverflow.com/q/2054939/995714), [is char signed or unsigned by default on iOS?](https://stackoverflow.com/q/20576300/995714). In gcc the signness of char can be changed with `-funsigned-char` or `-fsigned-char` – phuclv Jul 20 '21 at 17:15
  • @alfC -- the compiler converts the value of `c` to `int` to do the multiplication. Integer types get promoted at least to `int` or `unsigned int` for arithmetic operations. That `static_cast` just tells the compiler to do what it would have done anyway. (Well, except in the somewhat perverse case where `char` is the same size as `int` and unsigned). – Pete Becker Jul 20 '21 at 18:54
  • Thanks @phuclv, that was exactly the option I needed. – Gerald Jul 20 '21 at 19:04

3 Answers3

5

The char data type can be either signed or unsigned. In the case of the former (as seems to be the case when targeting x64), the c = i statement will cause overflow on the 129th iteration of the for loop (the maximum value for a signed char is 127) and the value it is being assigned has 'wrapped round' to a negative value.

However, when targeting arm64, your compiler appears to use an unsigned char type (with a range of 0 thru 255), so there is no overflow in that statement and the arithmetic proceeds 'as expected'.


To confirm (or otherwise) the above diagnosis, just check the value of the CHAR_MAX constant (defined in the <climits> header file) in your different build environments.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • That's exactly the issue! char was unsigned by default. Thanks for pointing that out. I learned something today. – Gerald Jul 20 '21 at 18:11
0

char is signed by standard.
However, ARM decided to make it unsigned by default decades ago.

It's that simple.

Jake 'Alquimista' LEE
  • 6,197
  • 2
  • 17
  • 25
-2

char is represented by 8 bit and it is signed. You can store 0-127, after that first bit is interpreted as a negative number. Maybe it is better to use uint8_t or similar instead of char.

Dzemo997
  • 328
  • 2
  • 5
  • You can't store 128 in a signed, 8-bit char type. The range is -128 thru +127. – Adrian Mole Jul 20 '21 at 16:38
  • You know about editing your own posts, right? – Adrian Mole Jul 20 '21 at 16:47
  • 2
    `char is represented by 8 bit and it is signed` **that's not correct**. `char` is represented by `CHAR_BIT` bits (which can be larger than 8) and is a signed or unsigned type depending on implementation. And your answer doesn't explain why the results are different between 2 platforms – phuclv Jul 20 '21 at 17:16
  • The overflow is not a problem, it's even intended in that case. Windows and Linux x64 behaved correctly. And changing the code was not possible, it's too widely use. – Gerald Jul 20 '21 at 18:12