【问题标题】:how to convert Math.Ceiling result to int?如何将 Math.Ceiling 结果转换为 int?
【发布时间】:2012-01-12 09:49:24
【问题描述】:

Math.Ceiling 返回double 因为double 可能存储更大的数字。 但是,如果我确定 int 类型能够存储结果,我应该如何转换?投射(int) Math.Ceiling(... 是否安全?

【问题讨论】:

  • 关于 int 和 maxsize 的说明:在设计数​​据库时,我一直对我的所有 int(identity/auto_number) 感到焦虑,但说真的,2,147,483,647 是一个相当大的数字......引用比尔盖茨的话:“ 640K 应该对任何人都足够了”。
  • @NahumLitvin 引用的问题是why 问题,我的问题是how 问题
  • ps 比尔盖茨关于 640k 的报价是一个神话......很多人似乎希望它是真的

标签: c#


【解决方案1】:

如果你确定你没有越过 int 的容量,这样做应该是完全安全的

int myInt = (int)Math.Ceiling(...);

如果您不确定界限,您可以使用long 而不是int

【讨论】:

  • 除了没有什么能阻止双打超出双打的界限
  • 我猜你的意思是长期超出界限...... ;) 但当然不是,但它提供了更多的游戏空间。如果他超过甚至更长,他只需将其保留为双重。
  • 当然,我的意思是双倍超出界限。愚蠢的错字^^
【解决方案2】:

根据 C++ 实践,我将使用以下内容。即使上限返回 99.99999...8 或 100.000000...1,也保证得到正确的结果

var result = (int)(Math.Ceiling(value) + 0.5);

如果您信任它的实现,下面的代码也应该可以工作

var result = Convert.ToInt32(value);

【讨论】:

  • 我真的需要+0.5吗?这就是我的问题。到目前为止,我似乎不需要那个丑陋的代码。
  • 您需要 + 0.5 以避免双舍入错误。例如 (int)103.919 是 104 而不是 103。Convert.ToInt32 没有这个问题,因为它向最接近的整数舍入。
  • Math.Ceiling() 返回一个整数值(双精度或十进制类型)。它不会返回分数,因此无需使用 0.5“重新舍入”它
【解决方案3】:

如果只考虑速度,那么 Int 输入和输出的 Math.Ceiling 会很慢。最快的是内联表达式。 2.4 秒 vs 33 毫秒。

警告:仅适用于正值 valuedivisor

A) 模数

这是我想出的一个,显然之前也被 C/C++ 开发人员发现过:

var ceilingResult = (value / divisor) + (value % divisor == 0 ? 0 : 1);

根据我自己的 10M 迭代基准,Math.Ceiling 大约需要 2.4 秒。在命名函数中调用此表达式需要约 380 毫秒,而将其作为直接内联表达式需要约 33 毫秒。

B) 仅简单算术

也可以考虑使用@mafu 的建议

var ceilingResult = (value + divisor - 1) / divisor;

请参阅此470x upvoted C++ answer 以获取参考和验证。还有https://stackoverflow.com/a/4175152/887092

C) DivRem

在查看这个答案时,https://stackoverflow.com/a/14878734/887092,我注意到让我想起 DivRem CPU 指令的评论。见https://docs.microsoft.com/en-us/dotnet/api/system.math.divrem?view=netframework-4.8Math.DivRem 应该解析为这样的 CPU 指令。

var quotient = Math.DivRem(value, divisor, out long remainder);
var ceilingResult = quotient + (remainder == 0 ? 0 : 1);

[我没有测试过这个]。请参阅https://stackoverflow.com/a/924160/887092,了解潜在的边缘情况(使用负 Int 数)

可能会对此进行进一步优化 - 可能是通过强制转换。在这个答案https://stackoverflow.com/a/924160/887092 中,使用了一个 if 条件语句 - 它们大致相同。


3的表现:

  • Modulus:有两个添加的操作,还有一个条件分支。
  • 算术:在序列中有一些额外的数学运算。
  • DivRem:基于 Modulus 方法。如果 C# 确实将 Math.DivRem 解析为单个 CPU 指令,这可能会更快。进一步的优化也是可能的。

我不确定这两者在各种架构上的表现如何。但现在您可以选择探索。


如果您希望将 Math.Floor 用于 Int 输入和输出,那就更简单了:

var floorResult = (value / divisor);

【讨论】:

  • 我必须强调,这仅在您使用 valuedivisor 进行除法时有效,并且当 valuedivisor 都是整数时。我的场景是,“加密给定数量的纯文本字节时需要多少个 AESEncryption 块”,其中纯文本字节数为valuedivisor 是 AESEncryption 块中的字节数。例如,块大小为 16 的 9 个字节将需要 2 个块。
  • 你为什么不用(value + divisor - 1) / divisor
  • @mafu 在我看来应该可行。如果是这样 - 这是一个很大的改进。
【解决方案4】:

我会选择

int x = (int)Math.Ceiling(0.9); // 1

【讨论】:

    【解决方案5】:

    如果您不确定,您可以随时添加 if 语句并检查您返回的数字是否高于 int.MaxValue

    【讨论】:

      【解决方案6】:

      int oInt = Convert.ToInt32(Math.Ceiling(value));

      由于Math.Ceiling 返回double,并且您想将其转换为int,请使用Convert 类。
      例子:

      double[] values= { Double.MinValue, -1.38e10, -1023.299, -12.98,
                         0, 9.113e-16, 103.919, 17834.191, Double.MaxValue };
      int result;
      
      foreach (double value in values)
      {
         try {
            result = Convert.ToInt32(value);
            Console.WriteLine("Converted the {0} value '{1}' to the {2} value {3}.",
                              value.GetType().Name, value,
                              result.GetType().Name, result);
         }
         catch (OverflowException) {
            Console.WriteLine("{0} is outside the range of the Int32 type.", value);
         }   
      }                                 
      //    -1.79769313486232E+308 is outside the range of the Int32 type.
      //    -13800000000 is outside the range of the Int16 type.
      //    Converted the Double value '-1023.299' to the Int32 value -1023.
      //    Converted the Double value '-12.98' to the Int32 value -13.
      //    Converted the Double value '0' to the Int32 value 0.
      //    Converted the Double value '9.113E-16' to the Int32 value 0.
      //    Converted the Double value '103.919' to the Int32 value 104.
      //    Converted the Double value '17834.191' to the Int32 value 17834.
      //    1.79769313486232E+308 is outside the range of the Int32 type.
      

      【讨论】:

      • 我在这里的任何地方都看不到Math.Ceiling。你读过这个问题吗?
      • 我不需要catchOverflowException,因为我确定我没有OverflowException。所以我更愿意传播这种异常,所以在我的情况下,演员应该可以工作。
      • 实际测试至少一些边缘情况的第一个答案(+1)
      猜你喜欢
      • 1970-01-01
      • 2017-11-25
      • 2023-03-19
      • 1970-01-01
      • 2012-07-05
      • 1970-01-01
      • 2011-06-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多