C# Как найти максимальное значение int не используя константу?

Рейтинг: 0Ответов: 3Опубликовано: 07.04.2023

Нужно найти максимальное значение типа int, используя только математические вычисления,не зная вес типа данных, без константы MaxValue. Тот же вопрос к типу double.

Ответы

▲ 1
int maxInt = (int)Math.Pow(2, 8 * sizeof(int)) - 1; // для int
double maxDouble = Math.Pow(2, 8 * sizeof(double) - 12) * (1 - Math.Pow(2, -52)) * Math.Pow(2, 1023); // для double

Если я правильно понял ваш вопрос

Объяснение:

int:

  • Тип int занимает 32 бита, из которых один бит используется для знака числа.
  • Максимальное значение без знака, которое можно представить 31 битами, равно 2^31 - 1.
  • Поскольку используется знаковое представление чисел, максимальное значение int равно половине от максимального значения без знака, т.е. (2^31 - 1)/2 = 2^30 - 1.

double:

  • Тип double использует 64 бита, из которых 1 бит используется для знака числа, 11 бит - для хранения порядка, а 52 бита - для хранения мантиссы.
  • Максимальное значение мантиссы равно (2^52 - 1), а максимальное значение порядка равно 2^10 - 1 = 1023.
  • Максимальное значение double можно получить, установив мантиссу в максимальное значение и порядок равный максимальному. Значение double вычисляется как 2^e * (1 + m/2^52), где e - порядок, m - мантисса.
  • Таким образом, максимальное значение double равно 2^1023 * (1 + (2^52 - 1)/2^52)
▲ 1

Не помню, допускают ли типы C# переполнение, но в абстрактном языке можно найти максимум для int так, если он допускает переполнение:

x = 1
x0 = x - 1
while(x > x0)
{
    x0 = x
    x *= 2
}
x = x0 - 1 + x0

То есть умножаем число в 2 раза пока оно не переполнится (уйдёт в минус). Максимум будет тогда в 2 раза умноженное число до переполнения, минус 1.

▲ 1

Для целых типов задача довольно простая, т.к. битовая структура числа однородна и все биты числа равнозначны. Битовое представление чисел со знаком и без знака не отличается, отличается только интерпретация этого представления. Для использования представления со знаком и инверсии чисел, используется так называемый дополнительный код и инверсия выполняется по правилу NOT(x)+1, для обеспечения единственности нуля. У такого подхода есть недостаток - наименьшее отрицательное число не может быть инвертировано в положительное, т.к. положительное число такой величины отсутствует, но есть и достоинство - старший бит становится индикатором знака числа, однако, в отличие от дробных чисел, нельзя просто изменить значение одного бита, чтобы получить из отрицательного положительное и наоборот.

Для поиска граничных значений достаточно использовать беззнаковое представление интересующего типа, операцию битового сдвига влево с последующим добавлением единицы битовым OR. Таким образом мы найдем максимальное значение беззнакового представления, а из него максимум и минимум для знакового представления, например так:

uint test = 1;
uint tmp;
do{
    tmp = test;
    test = (test << 1) | 1;
}
while (test != tmp);
Console.WriteLine($"uint max value = {test}");
Console.WriteLine($"int min value = {-(long)(test/2)-1}");//тут нужно явное приведение
//к знаковому типу для проверки типа ulong, т.к. он является самым большим из доступных
//и не может быть преобразован в более емкий тип со знаком автоматически.
Console.WriteLine($"int max value = {test/2}");

А вот для двоичных дробных чисел коими являются float и double задача общего решения не имеет, т.к. нужно заранее точно знать не только разрядность числа, но и количество, размер, взаимное расположение и назначение групп битов в числе, которые регламентирует стандарт IEEE-754.

Как частное решение с опорой на знание стандарта IEEE-754 можно посчитать длину группы бит экспоненты и сделать вывод о том, к какому типу из стандарта относится проверяемый тип. Сделать это можно например так:

var x = 1.0f;
int k = 0;
do
{
    k += 1;
    x *= 2;
}
while (x != x / 0.0); //тут можно было взять константу "Infinity",
                      //но ее легко получить простым делением на 0
var expLen = Math.Log(k, 2) + 1;
Console.WriteLine($"Exponent length {expLen} bit, x is {(expLen == 8 ? "float" : "double")}");

В обоих примерах начальное значение для проверяемого типа должно быть равно единице, т.к. мы исследуем не значения, а сам тип переменной.