【问题标题】:Test if a floating point number is an integer测试浮点数是否为整数
【发布时间】:2008-09-26 22:00:38
【问题描述】:

此代码有效(C# 3)

double d;
if(d == (double)(int)d) ...;
  1. 有没有更好的方法来做到这一点?
  2. 出于无关的原因,我想避免双重转换;除了这个还有什么好的方法吗? (即使它们没有那么好)

注意: 有几个人指出了(重要的)一点,即 == 在重定浮点时经常会出现问题。在这种情况下,我希望值在 0 到几百之间,并且它们应该是整数(非整数是错误),所以如果这些点“不应该”对我来说是个问题。

【问题讨论】:

    标签: c# floating-point numerical


    【解决方案1】:
    d == Math.Floor(d)
    

    换句话说,做同样的事情。

    注意:希望您知道在做这种事情时必须非常小心;浮点数/双精度数很容易累积微小的错误,导致精确比较(如这个)无缘无故地失败。

    【讨论】:

    • 不完全一样。我觉得好多了。如果 integr 数字很大,可以存储在 double 但不能存储在 int 中,转换为 int 将不起作用
    • 正如您所提到的,在进行任何类型的浮点相等比较时都应该非常小心 - 在大多数情况下,更好的策略是使用小的 epsilon 因子。
    • 我喜欢这个答案!但是在我的情况下,我还应该检测到溢出(或任何舍入错误),不过我会以更通用的形式提出问题。
    • @Cade Roux:如果您正在查看舍入操作,则精确的 FP 比较很有用。
    • @Mike F:另一种策略将使用 d == Math.Truncate(d) 因为它使用更简单的舍入模式。
    【解决方案2】:

    我认为这可行:

    if (d % 1 == 0) {
      //...
    }
    

    【讨论】:

    • 我只是有点厌倦了带有负数的 int mod,我不确定我是否想要为 FP 找出它们。
    • @BCS,你能说一下,“负数的 int mod”有什么问题吗?
    • 当一个或两个操作数为负时,整数模块至少有两种不同的解释。由于人们不记得当前使用的是哪种语言,因此存在很多错误代码。我不知道如果你也在 FP 中会发生什么。而且我怀疑答案有太多不同的“取决于”,无法安全使用。例如,如果没有详细阅读标准,我不确定在某些情况下(或某些语言版本)FP 不会在 mod 之前转换为 int。
    【解决方案3】:

    如果你的 double 是另一个计算的结果,你可能想要这样的东西:

    d == Math.Floor(d + 0.00001);
    

    这样,如果有轻微的舍入误差,它仍然会匹配。

    【讨论】:

    • @Khoth 请问,你能举出一个例子吗 (d==Math.Floor(d)) != (d==Math.Floor(d+0.00001)) ?
    • 您应该使用 double.Epsilon... 或者如果您正在考虑 x 多次操作 x*double.Epsilon。
    【解决方案4】:

    我无法回答问题的 C# 特定部分,但我必须指出,您可能遗漏了浮点数的一般问题。

    通常,整数在浮点数上没有很好的定义。出于同样的原因,在浮点数上没有很好地定义相等性。浮点计算通常包括舍入误差和表示误差。

    例如,1.1 + 0.6 != 1.7

    是的,这就是浮点数的工作方式。

    这里,1.1 + 0.6 - 1.7 == 2.2204460492503131e-16

    严格来说,您可以对浮点数进行的最接近相等比较的事情是将它们进行比较达到选定的精度

    如果这还不够,您必须使用十进制数表示、带有内置错误范围的浮点数表示或符号计算。

    【讨论】:

    • 一个很好的观点和很好的描述,但是在我的情况下,原始代码很好地定义了所需的行为。任何模仿它的东西都是有效的。
    • 即使是 0.1 != 0.1 就浮动而言。尾数必然会截断二进制值 0.0001100110011001100110011001100... 尝试 0.1F==0.1
    【解决方案5】:

    诸如“x == floor(x)”之类的简单测试在数学上可以确保对于任何固定精度的 FP 数都能正常工作。

    所有合法的固定精度 FP 编码都表示不同的实数,因此对于每个整数 x,至多有一个与它完全匹配的固定精度 FP 编码。

    因此,对于可以以这种方式表示的每个整数 x,我们必然有 x == floor(x),因为 floor(x) 根据定义返回最大的 FP 数 y,使得 y

    【讨论】:

      【解决方案6】:

      如果您只是要转换它,Mike F / Khoth 的回答很好,但不能完全回答您的问题。如果您要进行实际测试,并且它确实很重要,我建议您实施一些包含误差范围的方法。

      例如,如果您正在考虑金钱并且想要测试偶数美元,您可能会说(遵循 Khoth 的模式):

      if( Math.abs(d - Math.Floor(d + 0.001)) < 0.001)
      

      也就是说,取值与它的整数表示之差的绝对值,并确保它很小。

      【讨论】:

      • 或者 is_almost_integer(d,eps) 可以简单地返回 Math.Floor(d-eps)!=Math.Floor(d+eps)
      【解决方案7】:

      你不需要额外的(双)在那里。这有效:

      if (d == (int)d) {
       //...
      }
      

      【讨论】:

      • 行得通!顺便说一句,你确定它会完全一样吗?
      • 嗯。那这个呢? d= 3e30; if (d == (int)d) {
      • 是的,它不会完全按照我的要求做,但我会做我需要的。
      【解决方案8】:

      使用 Math.Truncate()

      【讨论】:

        【解决方案9】:

        这将让您选择您正在寻找的精度,加上或减去半个刻度,以考虑浮点漂移。比较也是完整的,这很好。

        static void Main(string[] args)
        {
            const int precision = 10000;
        
            foreach (var d in new[] { 2, 2.9, 2.001, 1.999, 1.99999999, 2.00000001 })
            {
                if ((int) (d*precision + .5)%precision == 0)
                {
                    Console.WriteLine("{0} is an int", d);
                }
            }
        }
        

        输出是

        2 is an int
        1.99999999 is an int
        2.00000001 is an int
        

        【讨论】:

          【解决方案10】:

          类似的东西

          double d = 4.0;
          int i = 4;
          
          bool equal = d.CompareTo(i) == 0; // true
          

          【讨论】:

            【解决方案11】:

            你能用这个吗

                bool IsInt(double x)
                {
                    try
                    {
                        int y = Int16.Parse(x.ToString());
                        return true;
                    }
                    catch 
                    {
                        return false;
                    }
                }
            

            【讨论】:

            • int.TryParse 会更容易使用,但也会产生(相对)较大的开销。
            • x.ToString() 输入某些字符串搜索逻辑可能会更好。
            • 我猜你可以做类似 if(x.tostring().indexof('.')!= -1) {true} else {false}
            • @Crash893:您需要 IndexOfAny('.', 'e', 'E'),因为某些值没有点(例如 1e15)。
            【解决方案12】:

            处理双精度...

            Math.Abs(d - Math.Floor(d)) <= double.Epsilon
            

            考虑以下情况,其中 小于 double.Epsilon 的值无法比较为零。

            // number of possible rounds
            const int rounds = 1;
            
            // precision causes rounding up to double.Epsilon
            double d = double.Epsilon*.75;
            
            // due to the rounding this comparison fails
            Console.WriteLine(d == Math.Floor(d));
            
            // this comparison succeeds by accounting for the rounding
            Console.WriteLine(Math.Abs(d - Math.Floor(d)) <= rounds*double.Epsilon);
            
            // The difference is double.Epsilon, 4.940656458412465E-324
            Console.WriteLine(Math.Abs(d - Math.Floor(d)).ToString("E15"));
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-10-01
              • 2011-08-13
              • 2013-11-29
              • 2013-02-25
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多