double a = 135.24; // a is set to 135.24000000000001 actually
double b = Math.Round(a, 0); // set to 135.0
double c = Math.Round(a, 1); // set to 135.19999999999999
double d = Math.Round(a, 2); // set to 135.24000000000001
double e = Math.Round(a, 3); // set to 135.24000000000001
double f = Math.Round(a, 4); // set to 135.24000000000001
double g = Math.Round(a, 5); // set to 135.24000000000001
double h = Math.Round(a, 10); // set to 135.24000000000001
double i = Math.Round(a, 14); // set to 135.24000000000001
double j = Math.Round(a, 2
, MidpointRounding.AwayFromZero ); // set to 135.24000000000001
double k = Math.Round(a, 2
, MidpointRounding.ToEven ); // set to 135.24000000000001
Sooooo,这意味着135.24不能用双精度表示,对吗?
答案 0 :(得分:5)
是的,135.24不能用double表示,因为double使用二进制指数表示法。
即:135.24可以以2为基数以指数形式表示为1.0565625 * 128 =(1 + 1/32 + 1/64 + 1/128 + 1/1024 + ...)*(2 ** 7)
表示不能完全表达,因为13524不会除以5.让我们看看:
135.24 = 13524/(10**2)
表示是有限的
<=>
存在整个x和n满足135.24 = x/(2**n)
135.24 = x/(2**n) 13524 / (10**2) = x / (2**n) 13524 * (2**n) = (10**2) * x 13524 * (2**n) = 2*2*5*5 * x
左侧没有“5”,所以无法完成 (称为算术的基本定理)
一般来说,只有在十进制数的素数因子分解中有足够数量的“五”时,有限二进制表示才是精确的。
现在有趣的部分:
double delta = 0.5;
while( 1 + delta > 1 )
delta /= 2;
Console.WriteLine( delta );
double的精度在1附近不同,在0附近不同,对于某些大数字不同。维基百科上的一些二进制表示示例:Double precision floating point format
但最重要的是内部处理器浮点堆栈可能比8字节(双倍)具有更强的更好的精度。如果数字不必转移到RAM并且被剥离到8个字节,我们就可以得到非常好的精度。
在不同的处理器(AMD,Intel)上测试类似的东西,语言(C,C ++,C#,Java)或编译器优化级别可以得到大约1e-16,1e-20甚至1e-320 < / p>
看看CIL /汇编程序/ jasmin代码,看看究竟发生了什么(例如:for C ++ g++ -S test.cpp
创建带有汇编程序代码的test.s
文件)
答案 1 :(得分:4)
这通常是浮点数的问题。如果您需要精确的数字表示(例如,用于计费,......),那么您应该使用十进制。 尝试下面的代码,你会发现你没有输出0,0.1,0.2,...... 1.0。
for(double i = 0; i <= 1.0; i += 0.001)
{
Console.WriteLine(i);
}
答案 2 :(得分:3)
尝试使用小数代替。浮点不是很精确(因此无法表示某些数字):)
答案 3 :(得分:0)
是的,它不能。这就是为什么还有一个名为decimal
的非整数数据类型。它需要不同的内存量,并且具有与double
不同的最小/最大数值范围,并且不能进行位转换*)加倍,但反过来它可以精确保持数字而不会出现任何失真。
*)也就是说,你不能把它复制为字节并推送到C ++代码。但是,您仍然可以cast
将其加倍并返回。请注意,演员阵容不准确,因为double
无法保留decimal
可以包含的某些数字,反之亦然
答案 4 :(得分:0)
你可以看到定义。圆函数定义为 -
public static double Round(double value, int digits, MidpointRounding mode)
{
if (digits < 0 || digits > 15)
throw new ArgumentOutOfRangeException("digits", Environment.GetResourceString("ArgumentOutOfRange_RoundingDigits"));
if (mode >= MidpointRounding.ToEven && mode <= MidpointRounding.AwayFromZero)
return Math.InternalRound(value, digits, mode);
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidEnumValue", (object) mode, (object) "MidpointRounding"), "mode");
}
private static unsafe double InternalRound(double value, int digits, MidpointRounding mode)
{
if (Math.Abs(value) < Math.doubleRoundLimit)
{
double num1 = Math.roundPower10Double[digits];
value *= num1;
if (mode == MidpointRounding.AwayFromZero)
{
double num2 = Math.SplitFractionDouble(&value);
if (Math.Abs(num2) >= 0.5)
value += (double) Math.Sign(num2);
}
else
value = Math.Round(value);
value /= num1;
}
return value;
}