3

I have a function print_number.

The function checks if in front of the number there exists '-', then it reverse the number and takes every digit and prints it. The algorithm works pretty good but if i give -2.147.483.648 ( which should be the bottom limit of an integer ) it pritns -0 and i don't know why.

#include<stdio.h>

void    print_char(char character)
{
    printf("%c",character);
}

void    print_number(int nr)
{

    int reverse=0;

    if (nr < 0)
    {
        print_char('-');
        nr *= -1;
    }

    while(nr > 9)
    {
        reverse = reverse * 10 + nr % 10;
        nr = nr / 10;
    }
    print_char(nr + '0');

    while(reverse)
    {
        print_char(reverse % 10 + '0');
        reverse = reverse / 10;
    }
}
nextdarius
  • 27
  • 1
  • 6

3 Answers3

1

When you are doing

if (nr < 0)
{
    print_char('-');
    nr *= -1;
}

It inverses negative number to the positive one. If you will run it for -2.147.483.648, you will receive

nr = 2.147.483.648 // == binary 1 0000000000000000000000000000000

As INT is 32 BIT variable in your architecture (and at least 16 BIT variable by the spec), so '1' overflows it and so on

nr = 0 // For gcc-like C realisation

And accepting the ISO9899 spec, this behaviour of signed int overflow is realisation-specified thing and may not be predicted in common.

Use long long value if you're needing to use your program for larger values. Something like:

#include<stdio.h>

void    print_char(char character)
{
    printf("%c",character);
}

void    print_number(long long nr)
{
    int reverse=0;

    if (nr < 0)
    {
        print_char('-');
        nr *= -1;
    }

    while(nr > 9)
    {
        reverse = reverse * 10 + nr % 10;
        nr = nr / 10;
    }
    print_char(nr + '0');

    while(reverse)
    {
        print_char(reverse % 10 + '0');
        reverse = reverse / 10;
    }
}

void main(void){
    print_number(-2147483648LL);
}

And test:

> gcc test.c
> ./a.out 
-2147483648
MobDev
  • 1,614
  • 17
  • 17
  • How will `long` help? Did you mean `long long`? – Weather Vane Aug 13 '17 at 14:33
  • `INT_MIN * -1` causes an overflow and the result is undefined. – Weather Vane Aug 13 '17 at 14:37
  • No, "long int" is enougth, see the demo I\ve attached on post update. long int makes variable twice larger than without long, so on it's enougth to store +2147483648 value. – MobDev Aug 13 '17 at 15:12
  • About undefined - yes, you are particularry right, but in really it will be calculated for 32 bits, so we can predict that it will be zero (1[overflow] 000...000[32 zeros]). – MobDev Aug 13 '17 at 15:21
  • No, it is undefined, so nothing can be "predicted". a 32-bit `int` cannot hold 33 bits. Your code prints`-0`. And `sizeof(long)` is 4 on my system, hence my remark about using `long long`. – Weather Vane Aug 13 '17 at 15:31
  • Ok, so you need to use long long :) – MobDev Aug 13 '17 at 15:47
  • Yes, I have checked the specification, the long long is the right cross-compilator version. I have fixed my answer and example. – MobDev Aug 13 '17 at 16:55
  • My compilation gives the wrong answer `2147483648` (not negative). Note that the compiler issues a warning for `-2147483648` because it is undefined behaviour: `2147483648` is not a value in range of `int` so it cannot be negated. Also see that MSVC has `#define INT_MIN (-2147483647 - 1)` in `limits.h` for this very reason. I suggest `print_number(-2147483648LL);` – Weather Vane Aug 13 '17 at 17:03
  • Which compilator have you used for it? Does it works ok with -2147483648LL ? – MobDev Aug 13 '17 at 17:10
  • Yes it does work, and solves compiler warnings. As I said your code has ***undefined behaviour*** which might work, might not work, or may have unpleasant side effects. `2147483648` is not an `int` value so it cannot be parsed correctly, let alone negated. – Weather Vane Aug 13 '17 at 17:13
  • @WeatherVane, about undefined result of MIN_INT*-1 - we need to know how this code compiles. In most cases in uses IMUL instruction for signed int multiplication, it correctly calculates young 32 bits, only 33th bit will be lost and OF/CF flags will be up. So we can predict zero-value here. Spec: http://felixcloutier.com/x86/IMUL.html – MobDev Aug 13 '17 at 17:35
  • "In most cases" I was not talking about what you hope the processor will do, but what C says on the tin. The overflow of signed integer arithmetic is undefined behaviour. `2147483648` *is not representable as a signed 32-bit `int`*. I am not going to say it again. – Weather Vane Aug 13 '17 at 22:37
  • Do you have a specification link with pointer to the undefined behaviour? :) – MobDev Aug 14 '17 at 15:54
  • This [question](https://stackoverflow.com/questions/3948479/integer-overflow-and-undefined-behavior) discusses integer overflow. Here is a [draft C spec](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) see section 6.2.5.9 for description of `unsigned` overflow, somewhere in there is a description of `signed` overflow. – Weather Vane Aug 14 '17 at 17:06
  • Here is [another link](https://stackoverflow.com/questions/19842215/wrap-around-explanation-for-signed-and-unsigned-variables-in-c) and you can google more for yourself. Note the difference between what your processor does, and what the C language specifies - which says that unsigned interger overflow essentially "wraps" but that signed integer overflow is undefined behaviour. – Weather Vane Aug 14 '17 at 17:10
  • Seems like the only answer for this question in spec is "..is implementation-defined or an implementation-defined signal is raised...", so you are right. – MobDev Aug 14 '17 at 22:49
  • `print_number(long long nr)` and `nr *= -1;` is UB of the minimal `long long`, which may be the same as `INT_MIN`. "As INT is 32 BIT variable" --> C does not specify that. – chux - Reinstate Monica Aug 15 '17 at 04:41
  • "print_number(long long nr) and nr *= -1; is UB of the minimal long long" - you're absolutelly right, but if author required to work with INT only, it's OK for the example to give understanding about the error reason. "AS INT is 32 BIT value" - OK, in author's architecture it is 32 BIT, by the spec - 16 BIT, byt anycase I've fixed my answer, tnx. "minimal long long, which may be the same as INT_MIN" --> C99 Spec (5.2.4.2 Numerical limits) says they can't, INT_MIN is at least -32767 AND LLINT_MIN is at least -9223372036854775807, so it will work for "-2.147.483.648" anycase. – MobDev Aug 15 '17 at 07:40
1

Firstly, the MAX and MIN range for an INT are -2,147,483,648 and 2,147,483,647 respectively.
Negating -2,147,483,648 means a positive value 2,147,483,648 would result in an overflow by 1 as it is out of bounds for the MAX range. This operation will result in the same value of -2,147,483,648.

Secondly, you might encounter an overflow during the integer reversing process.
Example, reversing 2147483647 causes an overflow after the intermediate result of 746384741.
Therefore, you should handle that by throwing an exception or returning 0.

Thirdly, your loop for reversing the number is inaccurate. It should loop till while(nr != 0)

Here's the complete code.

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

int main()
{
    void reverseNumber(int);

    reverseNumber(124249732);
    return 0;
}

void reverseNumber(int nr)
{
    printf("nr = %d\n", nr);

    int reverse = 0;
    bool neg = false;
    if (nr < 0) {
        neg = true;
        nr *= -1;
    }

    while (nr != 0) {
        int digit = nr % 10;
        int result = reverse * 10 + digit;

        if ((result - digit) / 10 != reverse) {
            printf("ERROR\n");
            exit(0);
        }

        reverse = result;
        nr = nr / 10;
    }

    if(neg) {
        printf("%c", '-');
    }
    printf("%d\n", reverse);
}
Devendra Lattu
  • 2,732
  • 2
  • 18
  • 27
0

nr *= -1; is a problme when nr == INT_MIN as that is signed integer overflow. The result is undefined behavior (UB). Best to avoid.

Wider integers are not always available.

Using OP's general, approach, do not change the sign of nr until it is reduced.

void print_number(int nr) {
  int reverse = 0;

  if (nr < 0) {
    print_char('-');
    //nr *= -1;
  }

  while (nr/10) {   // new test
    reverse = reverse * 10 + nr % 10;
    nr = nr / 10;
  }
  reverse = abs(reverse);  // reverse = |reverse|
  nr = abs(nr);            // nr = |nr|
  print_char(nr + '0');

  while (reverse) {
    print_char(reverse % 10 + '0');
    reverse = reverse / 10;
  }
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256