Задача на типы данных в C++

Проходит лишь 21 тест из 50. Не понимаю в чем ошибка. Пытался без прямого сравнения с границами, а через логарифмы - не получилось. Помогите, пожалуйста.

Определение подходящих типов:

Дано целое число. Требуется определить все подходящие для его хранения типы данных.

Входные данные:

Входные данные содержат единственное целое число n (−2⁶⁴ ≤ n ≤ 2⁶⁴).

Выходные данные:

Выведите все подходящие типы данных, в которых можно хранить число n.
Если их несколько, то выводите каждый новый тип в отдельной строке.
Типы данных следует выводить в том же порядке, что и в следующем списке:

char
unsigned char
short int
unsigned short int
int
unsigned int
long long
unsigned long long

Мой код:

#include <iostream>
#include <string>
#include <cmath>

using namespace std;


int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    long long int n;
    cin >> n;
     if (-256 <= n && n <= 255){
        cout << "char\n";
    }
    if (0 <= n && n <= 511){
        cout << "unsigned char\n";
    }
    if (-65536 <= n && n <= 65535){
        cout << "short int\n";
    }
    if (0 <= n && n <= 131071){
        cout << "unsigned short int\n";
    }
    if (-4294967296 <= n && n <= 4294967295){
        cout << "int\n";
    }
    if (0 <= n && n <= 8589934591){
        cout << "unsigned int\n";
    }
    cout << "long long\n";
    if (n >= 0){
        cout << "unsigned long long\n";
    }
    return 0;
}

Переписал код - всё равно не работает:

#include <iostream>
#include <string>
using namespace std;

bool les(string n, string d){
  if (n[0] == '-'){
    if (d[0] != '-'){
      return true;
    }
    return not les(n.substr(1), d.substr(1));
  }else{
    if (d[0] == '-'){
      return false;
    }
    if (n.length() < d.length()){
      return true;
    }
    if (n.length() > d.length()){
      return false;
    }
    return n <= d;
  }

}

int main(){
  ios::sync_with_stdio(0);
  cin.tie(0);

  string n;
  cin >> n;
  if (les("-128", n) && les(n, "127")) cout << "char\n";
  if (les("0", n) && les(n, "255")) cout << "unsigned char\n";
  if (les("-32768", n) && les(n, "32767")) cout << "short int\n";
  if (les("0", n) && les(n, "65535")) cout << "unsigned short int\n";
  if (les("-2147483648", n) && les(n, "2147483647")) cout << "int\n";
  if (les("0", n) && les(n, "4294967295")) cout << "unsigned int\n";
  if (les("-9223372036854775808", n) && les(n, "9223372036854775807")) cout << "long long\n";
  if (les("0", n) && les(n, "18446744073709551615")) cout << "unsigned long long\n";
}

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

Автор решения: Stanislav Volodarskiy

В старом коде из вопроса были ошибочно заданы границы всех типов. У некоторых типов границы не были заданы вовсе.

В новом коде есть ошибка в строке return not les(n.substr(1), d.substr(1));. Для равных отрицательных значений функция возврадает ложь, а должна истину. Вместо отрицания надо поменять местами аргументы. Справедливость требует заметить, что и я сделал такую же ошибку в своём решении, но потом исправил её.

Идеи для решения:

  • получить у компилятора границы типов;
  • считать число как строку;
  • научиться сравнить числа, заданные строками.

Вот ответ на сравнениях строк:

#include <iostream>
#include <limits>
#include <string>
#include <string_view>

bool le(const std::string_view &a, const std::string_view &b) {
    if (a[0] == '-') {
        if (b[0] == '-') {
            return le(b.substr(1), a.substr(1));
        }
        return true;
    }
    if (b[0] == '-') {
        return false;
    }
    if (a.size() < b.size()) {
        return true;
    }
    if (a.size() > b.size()) {
        return false;
    }
    return a <= b;
}

template <typename T>
void check(const char *name, const std::string_view &x) {
    static const auto min = std::to_string(std::numeric_limits<T>::min());
    static const auto max = std::to_string(std::numeric_limits<T>::max());
    if (le(min, x) && le(x, max)) {
        std::cout << name << '\n';
    }
}

int main() {
    std::string n;
    std::cin >> n;
    check<              char>(         "char"         , n);
    check<unsigned      char>("unsigned char"         , n);
    check<             short>(         "short int"    , n);
    check<unsigned     short>("unsigned short int"    , n);
    check<               int>(         "int"          , n);
    check<unsigned       int>("unsigned int"          , n);
    check<         long long>(         "long long int", n);
    check<unsigned long long>("unsigned long long int", n);
}
$ g++ -std=c++23 -pedantic -Wall -Wextra -Werror -Wwrite-strings -Wconversion temp.cpp 

$ echo -18446744073709551616 | ./a.out

$ echo -9223372036854775809 | ./a.out 

$ echo -9223372036854775808 | ./a.out
long long int

$ echo -2147483649 | ./a.out
long long int

$ echo -2147483648 | ./a.out
int
long long int

$ echo -32769 | ./a.out
int
long long int

$ echo -32768 | ./a.out
short int
int
long long int

$ echo -129 | ./a.out
short int
int
long long int

$ echo -128 | ./a.out
char
short int
int
long long int

$ echo 0 | ./a.out
char
unsigned char
short int
unsigned short int
int
unsigned int
long long int
unsigned long long int

$ echo 127 | ./a.out
char
unsigned char
short int
unsigned short int
int
unsigned int
long long int
unsigned long long int

$ echo 128 | ./a.out
unsigned char
short int
unsigned short int
int
unsigned int
long long int
unsigned long long int

$ echo 255 | ./a.out
unsigned char
short int
unsigned short int
int
unsigned int
long long int
unsigned long long int

$ echo 256 | ./a.out
short int
unsigned short int
int
unsigned int
long long int
unsigned long long int

$ echo 32767 | ./a.out
short int
unsigned short int
int
unsigned int
long long int
unsigned long long int

$ echo 32768 | ./a.out
unsigned short int
int
unsigned int
long long int
unsigned long long int

$ echo 65535 | ./a.out
unsigned short int
int
unsigned int
long long int
unsigned long long int

$ echo 65536 | ./a.out
int
unsigned int
long long int
unsigned long long int

$ echo 2147483647 | ./a.out
int
unsigned int
long long int
unsigned long long int

$ echo 2147483648 | ./a.out
unsigned int
long long int
unsigned long long int

$ echo 4294967295 | ./a.out
unsigned int
long long int
unsigned long long int

$ echo 4294967296 | ./a.out
long long int
unsigned long long int

$ echo 9223372036854775807 | ./a.out
long long int
unsigned long long int

$ echo 9223372036854775808 | ./a.out
unsigned long long int

$ echo 18446744073709551615 | ./a.out
unsigned long long int

$ echo 18446744073709551616 | ./a.out

$
→ Ссылка
Автор решения: Qwertiy
if (-256 <= n && n <= 255){
   cout << "char\n";
}

Ну вообще-то char - это

if (-128 <= n && n <= 127) {

Остальные все числа - так же.


В условии есть подозрительные границы про 2**64 включительно - это не влезет в int64, а 2 значения по модулю влезут и в uint64.

→ Ссылка
Автор решения: Qwertiy

Я всё-таки намутил проверку без использования строк с числами не длиннее 64 бит:

tio.run

#include <cstdio>

int main()
{
  int neg, len;
  unsigned long long x, y;

  scanf(" ");
  scanf("-%n", &(neg=0));
  scanf("%19llu%n", &x, &len);

  if (len == 19 && scanf("%llu", &y) == 1 && !(x=x*10+y))
    return 0;

  #define CHECK(sign, type)                                 \
    do                                                      \
    {                                                       \
      bool sig = (sign type)~(sign type)0 < (sign type)0;   \
      sign type max = (unsigned type)~(type)0 >> sig;       \
                                                            \
      if (neg ? sig && x-1 <= max : x <= max)               \
        printf("%s" #type "\n", sig ? "" : #sign " ");      \
    }                                                       \
    while(0)

  CHECK(signed, char);
  CHECK(unsigned, char);
  CHECK(signed, short int);
  CHECK(unsigned, short int);
  CHECK(signed, int);
  CHECK(unsigned, int);
  CHECK(signed, long long);
  CHECK(unsigned, long long);

  return 0;
}

Для любителей сомнительных char'ов в духе -funsigned-char:

CHECK(, char);
→ Ссылка