C#中“%”操作对数字类型double的含义

时间:2011-11-12 16:06:25

标签: c# modulo

最近我发现C#的运算符%适用于double。尝试了一些事情,毕竟想出了这个测试:

class Program
{
    static void test(double a, double b)
    {
        if (a % b != a - b * Math.Truncate(a / b))
        {
            Console.WriteLine(a + ", " + b);
        }
    }
    static void Main(string[] args)
    {
        test(2.5, 7);
        test(-6.7, -3);
        test(8.7, 4);
        //...
    }
}

此测试中的所有内容都有效。 a % b是否始终等同于a - b*Math.Round(a/b)?如果没有,请向我解释这个运营商是如何运作的。

编辑:回答詹姆斯L,我知道这是一个模数运算符和一切。我很好奇它是如何使用双,我理解的整数。

4 个答案:

答案 0 :(得分:6)

模数运算符以与整数相同的方式处理浮点值。所以请考虑一个简单的例子:

4.5 % 2.1

现在,4.5 / 2.1约等于2.142857

因此,除法的整数部分是2.从4.5减去2 * 2.1并且你得到了剩余的0.3。

当然,此过程受浮点表示问题的影响,因此要注意 - 您可能会看到意外的结果。例如,请参阅Stack Overflow上的此问题:Floating Point Arithmetic - Modulo Operator on Double Type


  

%b是否总是等于a - b * Math.Round(a / b)?

不,不是。这是一个简单的反例:

static double f(double a, double b)
{
    return a - b * Math.Round(a / b);
}

static void Main(string[] args)
{
    Console.WriteLine(1.9 % 1.0);
    Console.WriteLine(f(1.9, 1.0));
    Console.ReadLine();
}

关于如何指定模数运算符的精确细节,您需要参考C#规范 - earlNameless's answer为您提供指向它的链接。

据我了解,a % ba - b*Math.Truncate(a/b)基本等效,模浮点精度。

答案 1 :(得分:4)

来自C# Language Specifications第200页:

浮点余数:

float operator %(float x, float y); 
double operator %(double x, double y); 

下表列出了非零有限值,零,无穷大和NaN的所有可能组合的结果。在表中,x和y是正有限值。 z是x%y的结果,计算为x - n * y,四舍五入到最接近的可表示值,其中n是小于或等于x / y的最大整数。这种计算余数的方法类似于用于整数操作数的方法,但不同于IEC 60559定义(其中n是最接近x / y的整数)。

答案 2 :(得分:2)

来自the MSDN page

  

模数运算符(%)计算除以后的余数   第二个操作数。所有数字类型都有预定义模数   运算符。

  

请注意与double类型相关的舍入错误。

答案 3 :(得分:0)

使用短语“ modulo float point c#”进行搜索会在Stack Overflow中显示很多条目,其中大多数很好地说明了浮点精度如何使事情变得复杂。我没有发现任何建议来解决这种简单实用的方法。我出于自己的目的想到的是以下模函数:

public static double modulo( double a, double b, double num_sig_digits = 14 )
{
   double  int_closest_to_ratio
         , abs_val_of_residue
         ;

   if ( double.IsNaN( a )
      || double.IsNaN( b )
      || 0 == b
      )
   {
      throw new Exception( "function modulo called with a or b == NaN or b == 0" );
   }

   if ( b == Math.Floor( b ) )
   {
      return (a % b);
   }
   else
   {
      int_closest_to_ratio = Math.Round( a / b );
      abs_val_of_residue = Math.Abs( a - int_closest_to_ratio * b );
      if ( abs_val_of_residue < Math.Pow( 10.0, -num_sig_digits ) )
      {
         return 0.0;
      }
      else
      {
         return abs_val_of_residue * Math.Sign( a );
      }
   }
}

以下是一些示例结果:

modulo(0.5,0.1,17)= 0

modulo(0.5,-0.1,16)= 0

modulo(-0.5,0.1,15)= 0

modulo(-0.5,-0.1,14)= 0

modulo(0.52,0.1,16)= 0.02

modulo(0.53,-0.1,15)= 0.03

modulo(-0.54,0.1,14)= -0.04

modulo(-0.55,-0.1,13)= -0.05

modulo(2.5,1.01,17)= 0.48

modulo(2.5,-1.01,16)= 0.48

modulo(-2.5,1.01,15)= -0.48

modulo(-2.5,-1.01,14)= -0.48

模(0.599999999999977,0.1%,16)= 2.35367281220533E-14

模(0.599999999999977,0.1%,15)= 2.35367281220533E-14

模(0.599999999999977,0.1%,14)= 2.35367281220533E-14

取模(0.599999999999977,0.1,13)= 0

取模(0.599999999999977,0.1,12)= 0