%"的含义在c#中用于数字类型double的操作
本文关键字:数字 用于 类型 操作 double quot | 更新日期: 2023-09-27 18:14:10
最近我发现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)
吗?如果没有,请给我解释一下这个操作符是如何工作的。
编辑:回答James L, 我理解这是一个模算子和一切。我只好奇它是如何工作的双精度,我理解的整数。
模数运算符对浮点数的操作方式与对整数的操作方式相同。考虑一个简单的例子:
4.5 % 2.1
现在,4.5/2.1近似等于2.142857
所以除法的整数部分是2。4.5减去2*2.1,剩下0.3。
当然,这个过程受到浮点可表示性问题的影响,所以要小心——你可能会看到意想不到的结果。例如,请参阅这里关于堆栈溢出的问题:浮点算术-双类型的模运算符a % 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的答案给了你一个链接。
这是我的理解,a % b
基本上是等效的,模浮点精度,a - b*Math.Truncate(a/b)
。
选自c#语言规范第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的整数)。
从MSDN页面:
模数运算符(%)计算除其后的余数第一个操作数乘以第二个操作数。所有数值类型都有预定义的模数运营商。
和
注意与double类型相关的舍入错误。
搜索短语"模浮点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 );
}
}
}
以下是一些示例结果:
模(0.5,0.1,17)= 0
模(0.5,-0.1,16)= 0
模(-0.5,0.1,15)= 0
模(-0.5,-0.1,14)= 0
模(0.52,0.1,16)= 0.02
模(0.53,-0.1,15)= 0.03
模(-0.54,0.1,14)= -0.04
模(-0.55,-0.1,13)= -0.05
模(2.5,1.01,17)= 0.48
模(2.5,-1.01,16)= 0.48
模(-2.5,1.01,15)= -0.48
模(-2.5,-1.01,14)= -0.48
modulo(0.59999999999999977, 0.1, 16) = 2.35367281220533E-14
modulo(0.59999999999999977, 0.1, 15) = 2.35367281220533E-14
modulo(0.59999999999999977, 0.1, 14) = 2.35367281220533E-14
模(0.59999999999999977,0.1,13)= 0
modulo(0.59999999999999977, 0.1, 12) = 0