Meaning of “%” operation in C# for the numeric type double

删除回忆录丶 提交于 2020-01-02 02:03:14

问题


Recently I discovered that C#'s operator % is applicable to double. Tried some things out, and after all came up with this test:

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);
        //...
    }
}

Everything in this test works. Is a % b always equivalent to a - b*Math.Round(a/b)? If not, please explain to me how this operator really works.

EDIT: Answering to James L, I understand that this is a modulo operator and everything. I'm curious only about how it works with double, integers I understand.


回答1:


The modulus operator works on floating point values in the same way as it does for integers. So consider a simple example:

4.5 % 2.1

Now, 4.5/2.1 is approximately equal to 2.142857

So, the integer part of the division is 2. Subtract 2*2.1 from 4.5 and you have the remainer, 0.3.

Of course, this process is subject to floating point representability issues so beware – you may see unexpected results. For example, see this question asked here on Stack Overflow: Floating Point Arithmetic - Modulo Operator on Double Type


Is a % b always equivalent to a - b*Math.Round(a/b)?

No it is not. Here is a simple counter example:

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();
}

As to the precise details of how the modulus operator is specified you need to refer to the C# specification – earlNameless's answer gives you a link to that.

It is my understanding that a % b is essentially equivalent, modulo floating point precision, to a - b*Math.Truncate(a/b).




回答2:


From C# Language Specifications page 200:

Floating-point remainder:

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

The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaN’s. In the table, x and y are positive finite values. z is the result of x % y and is computed as x – n * y, rounded to the nearest representable value, where n is the largest integer that is less than or equal to x / y. This method of computing the remainder is analogous to that used for integer operands, but differs from the IEC 60559 definition (in which n is the integer closest to x / y).




回答3:


From the MSDN page :

The modulus operator (%) computes the remainder after dividing its first operand by its second. All numeric types have predefined modulus operators.

And

Note the round-off errors associated with the double type.




回答4:


Searching with the phrase "modulo floating point c#" brings up quite a few entries in Stack Overflow, most of them explaining nicely how floating point precision complicates things. I did not recognize any suggestion for a simple practical way to handle that. What I came up with for my own purposes is the following modulo function:

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 );
      }
   }
}

Following are some sample results:

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

modulo( 0.599999999999977, 0.1, 16 ) = 2.35367281220533E-14

modulo( 0.599999999999977, 0.1, 15 ) = 2.35367281220533E-14

modulo( 0.599999999999977, 0.1, 14 ) = 2.35367281220533E-14

modulo( 0.599999999999977, 0.1, 13 ) = 0

modulo( 0.599999999999977, 0.1, 12 ) = 0



来源:https://stackoverflow.com/questions/8105613/meaning-of-operation-in-c-sharp-for-the-numeric-type-double

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!