Наибольшее значение «float» меньше или равно `int.MaxValue` - PullRequest
2 голосов
/ 03 июля 2019

Я пытаюсь написать функцию преобразования, которая принимает float и возвращает int, которая в основном выполняет насыщенное преобразование.Если оно больше int.MaxValue, то должно быть возвращено int.MaxValue, аналогично для int.MinValue.

Я бы предпочел не перехватывать исключения как часть моего обычного управления потоком, а вместо этого просто явно проверять границы, за исключением того, что я не уверен, какова верхняя граница для наибольшего числа с плавающей точкой, которое может храниться в int.меньше чем int.MaxValue, так как число с плавающей запятой имеет меньшую точность, чем int для значений такого размера.

В основном я ищу ... in:

float largestFloatThatCanBeStoredInAnInt = ...

Ответы [ 5 ]

2 голосов
/ 03 июля 2019

Давайте проведем эксперимент :

  float best = 0f;

  for (int i = 2147483000; ; ++i)
  {
    float f = (float)i;

    try
    {
      checked
      {
        int v = (int)f;
      }

      best = f;
    }
    catch (OverflowException)
    {
      string report = string.Join(Environment.NewLine, 
        $"   max float = {best:g10}",
        $"min overflow = {f:g10}",
        $"     max int = {i - 1}");

      Console.Write(report);

      break;
    }
  }

Результат

   max float = 2147483520
min overflow = 2147483650
     max int = 2147483583

Таким образом, мы можем заключить, что максимальное значение float, которое может быть приведено к int, равно 2147483520. Максимум int, который может быть приведен к float и обратно к int, равен 2147483583; если мы попытаемся разыграть 2147483583 + 1 = 2147483584, мы получим 2147483650f, который выдаст исключение, если мы попытаемся привести его к int.

int overflow = 2147483583 + 1;
int back = checked((int)(float) overflow); // <- throws exception

или даже

float f_1 = 2147483583f;        // f_1 == 2147483520f (rounding)
  int i_1 = checked((int) f_1); // OK

float f_2 = 2147483584f;        // f_2 == 2147483650f (rounding) > int.MaxValue
  int i_2 = checked((int) f_2); // throws exception

Наконец, float в int преобразование (без исключений; int.MaxValue или int.MinValue, если float имеет значение вне диапазона ):

  // float: round errors (mantissa has 23 bits only: 23 < 32) 
  public static int ClampToInt(this float x) =>
      x >  2147483520f ? int.MaxValue 
    : x < -2147483650f ? int.MinValue
    : (int) x;

  // double: no round errors (mantissa has 52 bits: 52 > 32)
  public static int ClampToInt(this double x) =>
      x > int.MaxValue ? int.MaxValue 
    : x < int.MinValue ? int.MinValue
    : (int) x;
2 голосов
/ 03 июля 2019

Я бы посоветовал вам просто жестко закодировать его как правильный тип данных:

var largestFloatThatCanBeStoredInAnInt = 2147483000f;

2 147 483 000 - это наибольшее значение, которое вы можете хранить в формате с плавающей запятой, которое меньше int.MaxValue

1 голос
/ 03 июля 2019

Этот подход обходит проблему:

public static int ClampToInt(this float x)
{
    const float maxFloat = int.MaxValue;
    const float minFloat = int.MinValue;

    return x >= maxFloat ? int.MaxValue : x <= minFloat ? int.MinValue : (int) x;
}

Здесь важно использовать >=.Используйте только >, и вы пропустите (float) int.MaxValue, а затем, когда вы выполните обычное приведение, вы обнаружите (int) (float) int.MaxValue == int.MinValue, что в результате заставит эту функцию вернуть неправильное значение.

0 голосов
/ 03 июля 2019

Почему бы вам просто не проверить пределы?

if(myFloat > int.MaxValue)
 return int.MaxValue;

if(myFloat < int.MinValue)
 return int.MinValue;
0 голосов
/ 03 июля 2019

Разве это не сработает?

float largestFloatThatCanBeStoredInAnInt = (float)int.MaxValue - 1;

Это выражение верно:

(float)int.MaxValue - 1 < int.MaxValue
...