最大 "float" 小于或等于 `int.MaxValue`

Largest "float" less than or equal to `int.MaxValue`

我正在尝试编写一个采用 float 和 returns 和 int 的转换函数,它基本上进行饱和转换。如果它大于 int.MaxValue,那么应该返回 int.MaxValue,对于 int.MinValue 也是如此。

我宁愿不将异常捕获作为正常流程控制的一部分,而只是显式检查边界,除非我不确定上限是多少,因为可以存储在 int 中的最大浮点数较小比 int.MaxValue 因为 float 的精度低于 int 对于大小的值。

基本上我在寻找...

float largestFloatThatCanBeStoredInAnInt = ...

那不行吗?

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

这个表达式是正确的:

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

我建议您 hard-code 它作为正确的数据类型:

var largestFloatThatCanBeStoredInAnInt = 2147483000f;

2,147,483,000 是您可以存储在小于 int.MaxValue

的浮点数中的最大值

这种方法解决了这个问题:

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,结果会使这个函数 return 出错值。

让我们进行一个实验:

  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

所以我们可以得出结论,可以转换为int的最大float2147483520。可以转换为 float 并返回到 int 的最大 int2147483583; 如果我们尝试转换 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

最后,floatint 的转换(没有例外;int.MaxValueint.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;