Неожиданная Runtime error в системе автотестов

Решаю небольшую задачу:

Составить программу для работы с целыми числами в троичной системе счисления. Для хранения таких чисел использовать стандартный тип long int, но ограничиться только цифрами 0,1,2. Учесть, что при таком представлении можно работать только с числами, ограниченными диапазоном типа long int. В качестве диапазона чисел в троичной системе счисления можно взять значения от –2 122 222 222 до 2 122 222 222. Составить функции для выполнения простейших арифметических операций и вычислить значение выражения ab^2-a(b-a)+b (числа a, b вводятся с консоли в троичной системе счисления).
В случаях возникновения переполнения, вместо значения результата выводить символ «!» и код той операции, при которой произошло переполнение:
1 – при сложении,
2 – при вычитании,
3 – при умножении,
4 – при возведении в степень.

Собственно реализовал я ее, можно сказать, "в лоб". Все функции просто переводят число из троичной системы в десятичную, вычисляют, переводят обратно. Добавил коды возврата, сигнализирующие об ошибках. Собственно, вот:

#include <stdio.h>

#define INT32_MAX 2147483647
#define NO_ERROR 0b0
#define OVERFLOW 0b1
#define ZERO_DIVISION 0b10

int toBase3(int k, int* res) {
    if (k < -52487 || k > 52487) return OVERFLOW;
    int p = 1;

    while (k) {
        *res += p * (k % 3);
        p *= 10;
        k /= 3;
    }

    return NO_ERROR;
}

int fromBase3(int k) {
    int res = 0;
    int p = 1;
    while (k) {
        res += p * (k % 10);
        p *= 3;
        k /= 10;
    }

    return res;
}

int sum3(int a, int b, int* exitCode) {
    int a10 = fromBase3(a);
    int b10 = fromBase3(b);

    int res10 = a10 + b10;
    int res3 = 0;
    *exitCode = toBase3(res10, &res3);
    return res3;
}


int sub3(int a, int b, int* exitCode) {
    int a10 = fromBase3(a);
    int b10 = fromBase3(b);

    int res10 = a10 - b10;
    int res3 = 0;
    *exitCode = toBase3(res10, &res3);
    return res3;
}


int mul3(int a, int b, int* exitCode) {
    if (a == 0 || b == 0) {
        *exitCode = NO_ERROR;
        return 0;
    }
    int a10 = fromBase3(a);
    int b10 = fromBase3(b);

    if (a10 > INT32_MAX / b10) {
        *exitCode = OVERFLOW;
        return 0;
    }
    int res10 = a10 * b10;
    int res3 = 0;
    *exitCode = toBase3(res10, &res3);
    return res3;
}


int div3(int a, int b, int* exitCode) {
    if (b == 0) {
        *exitCode = ZERO_DIVISION;
        return 0;
    }
    int a10 = fromBase3(a);
    int b10 = fromBase3(b);

    int res10 = a10 / b10;
    int res3 = 0;
    *exitCode = toBase3(res10, &res3);
    return res3;
}


int pow3(int a, unsigned short p, int* exitCode) {
    *exitCode = NO_ERROR;
    if (a == 0) return 0;
    if (p == 0) return 1;

    int a10 = fromBase3(a);
    int res10 = a10;
    unsigned short curPow = 1;
    while (curPow < p) {
        if (res10 > INT32_MAX / a10) {
            *exitCode = OVERFLOW;
            return 0;
        }

        res10 *= a10;
        ++curPow;
    }

    int res3 = 0;
    *exitCode = toBase3(res10, &res3);
    return res3;
}

int main() {
    int a;
    int b;
    scanf("%d %d", &a, &b);

    //ab^2 - a(b - a) + b
    int res = 0;
    int exitCode;

    res = pow3(b, 2, &exitCode);
    if (exitCode & OVERFLOW) {
        printf("!4\n");
        return OVERFLOW;
    }

    res = mul3(res, a, &exitCode);
    if (exitCode & OVERFLOW) {
        printf("!3\n");
        return OVERFLOW;
    }

    int temp = sub3(b, a, &exitCode);
    if (exitCode & OVERFLOW) {
        printf("!2\n");
        return OVERFLOW;
    }

    temp = mul3(temp, a, &exitCode);
    if (exitCode & OVERFLOW) {
        printf("!3\n");
        return OVERFLOW;
    }

    res = sub3(res, temp, &exitCode);
    if (exitCode & OVERFLOW) {
        printf("!2\n");
        return OVERFLOW;
    }

    res = sum3(res, b, &exitCode);
    if (exitCode & OVERFLOW) {
        printf("!1\n");
        return OVERFLOW;
    }

    printf("%d\n", res);
    return NO_ERROR;
}

При загрузке его в тестовую систему, получаю половину правильно пройденных тестов и половину Runtime error. Честно говоря, не вижу, где она может происходить. Компилятор предупреждений не дает, отладчик тоже. Я так и не смог подобрать тесты, чтобы сломать программу. Буду рад, если поможете найти такие.


PS: есть небольшой шанс ошибок в тестовой системе, так как подобные случаи уже встречались: при загрузке программы от имени другого пользователя пропадала Runtime error.


Ответы (1 шт):

Автор решения: Стас

Итак, оказалось, что код выше содержал две проблемы:

  • Первая, как указал @wololo, действительно заключалась в неверной проверке на переполнение типа данных при отрицательных входных значениях. Исправил добавлением модуля.
  • Вторая же была связана с тестирующей системой: оказалось, что каждый раз, когда main() возвращает ненулевой код, тест считается проваленным независимо от вывода программы в консоль. Замена всех return code; на return 0; исправила проблему.

Спасибо за помощь)

→ Ссылка