这段代码是可行的(C# 3)
double d;
if(d == (double)(int)d) ...;
- 有更好的做法吗?
- 出于其他原因,我想避免双重转换,那么除此之外还有什么好方法吗?(即使它们不那么好)
注意: 尽管下面的操作可能不是最佳方案,但由于浮点数等于符号的误差问题,在这种情况下,期望值在0到几百之间,而且它们应该是整数(非整数是错误的)。
这段代码是可行的(C# 3)
double d;
if(d == (double)(int)d) ...;
注意: 尽管下面的操作可能不是最佳方案,但由于浮点数等于符号的误差问题,在这种情况下,期望值在0到几百之间,而且它们应该是整数(非整数是错误的)。
d == Math.Floor(d)
换句话说,它做的是同样的事情。
顺便提醒一下:希望你意识到在进行这种操作时必须非常小心;浮点数/双精度数很容易积累微小误差,导致精确比较(例如此处)无法明显失败。
我认为这样做是可行的:
if (d % 1 == 0) {
//...
}
1.1 + 0.6 != 1.7
。1.1 + 0.6 - 1.7 == 2.2204460492503131e-16
。d == Math.Floor(d + 0.00001);
这样,即使存在轻微的四舍五入误差,它仍将匹配。
一个简单的测试,如 'x == floor(x)',在任何固定精度的浮点数中都可以数学保证正确运行。
所有合法的固定精度浮点编码表示不同的实数,因此对于每个整数x,最多只有一个固定精度浮点编码与之完全匹配。
因此,对于每个可以用这种方式表示的整数x,我们必须有 x == floor(x),因为按照定义,floor(x)返回最大的FP数y,使得y <= x且y表示一个整数;所以floor(x)必须返回x。
如果你只是要进行转换,Mike F / Khoth的回答很好,但并没有完全回答你的问题。如果你要实际测试,并且这非常重要,我建议你实现一个包括误差范围的东西。
例如,如果你考虑到钱的因素,想要测试是否为整数美元金额,你可以按照Khoth的模式说:
if( Math.abs(d - Math.Floor(d + 0.001)) < 0.001)
您不需要在这里加上额外的(双倍)内容。这样就可以正常工作:
if (d == (int)d) {
//...
}
d= 3e30; if (d == (int)d) {
- tzot使用 Math.Truncate()
这将让您选择所需的精度,加上或减去半个刻度,以解决浮点漂移问题。比较也是整数的,这很好。
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
为了处理双精度的精度问题...
Math.Abs(d - Math.Floor(d)) <= 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"));