Проблема аддитивного подхода в том, что он очень подвержен ошибкам округления с плавающей запятой.
Рассмотрим этот простой подход для + ve x
и d
:
double xfloor = Math.floor(x);
int count = 0;
do
{
x += d;
count += 1;
System.out.println(count + " " + x);
}
while(Math.floor(x) == xfloor);
System.out.format("Result: %d%n", count);
При x = 0
и d = 0.1
это дает:
1 0.1
2 0.2
3 0.30000000000000004
4 0.4
5 0.5
6 0.6
7 0.7
8 0.7999999999999999
9 0.8999999999999999
10 0.9999999999999999
11 1.0999999999999999
Result: 11
Что, очевидно, неверно.
В качестве альтернативы мы можем использовать деление, чтобы определить количество необходимых шагов. Мы должны быть осторожны, чтобы различать guish между случаями, когда x
и d
имеют одинаковые или противоположные знаки.
static int minAddFloorChange(double x, double d)
{
if(d == 0) throw new IllegalArgumentException();
double xa = Math.abs(x);
double da = Math.abs(d);
if(x == 0 || (Math.signum(x) == Math.signum(d)))
{
double xfrac = (xa == Math.rint(xa)) ? 1 : Math.ceil(xa) - xa;
double count = xfrac / da;
return (int)Math.ceil(count);
}
else
{
double xfrac = (x == Math.rint(xa)) ? 0 : xa - Math.floor(xa);
double count = xfrac / da;
return Math.max(1, (int)Math.ceil(count));
}
}
Обратите внимание, я не утверждаю, что этот подход полностью защищен от ошибок округления - вам нужен кто-то с большим знанием формата IEEE 754 , чем у меня чтобы решить эту проблему.
Тест:
static void test(double x, double d)
{
System.out.println(x + " : " + d + " = " + minAddFloorChange(x, d));
}
public static void main(String[] args)
{
test(0, 0.1);
test(0, -0.1);
test(1, 0.1);
test(-1, -0.1);
test(1, -0.1);
test(-1, 0.1);
test(0, +Double.MIN_VALUE);
test(0, -Double.MIN_VALUE);
test(1, Double.MIN_VALUE);
test(-1, -Double.MIN_VALUE);
test(1, -Double.MIN_VALUE);
test(-1, Double.MIN_VALUE);
}
Выход:
0.0 : 0.1 = 10
0.0 : -0.1 = 10
1.0 : 0.1 = 10
-1.0 : -0.1 = 10
1.0 : -0.1 = 1
-1.0 : 0.1 = 1
0.0 : 4.9E-324 = 2147483647
0.0 : -4.9E-324 = 2147483647
1.0 : 4.9E-324 = 2147483647
-1.0 : -4.9E-324 = 2147483647
1.0 : -4.9E-324 = 1
-1.0 : 4.9E-324 = 1