Tuesday, March 01, 2011

Неожиданные эффекты арифметики с плавающей запятой

Ниже приведён код на Java, хотя думаю, что похожие эффекты присущи любому языку реализующую арифметику с плавающей запятой.

Ниже есть продолжение.


//Long.MAX_VALUE/10-100
double d = 922337203685477480L; 
//Long.MAX_VALUE/10-50
double d2 = 922337203685477530L; 
System.out.println(d==d2);
System.out.println(d-d2);


1. Вопрос: что распечет программа?

Подсказка:

System.out.println(Math.ulp(d));

выдаст 128. (предполагается JDK 1.5+)


Если вы почитаете JavaDoc функции Math.ulp(d); вы увидите, что 2048.0 есть расстояние между d и следующий числом double.

Ответ:





true
0.0


2. Сколько чисел в интервале [1,2]?
Подсказка 1: не мощность континуума, речь идёт опять об арифметике с плавающей запятой, пусть будут числа с обычной точности - float.

Подсказка 2 (предпологается JDK 1.6+ 1.5+).

float x = 1.0F;
long num = 0;
while (x <= 2.0) {
num++;
System.out.println(x);
x = Math.nextUp(x);
}
System.out.println(num);
UPDATE: 03-03-2011: Если вы используете JDK 1.5 вы можете сами написать функции nextUp().
/**
* Answers the next larger double value to d.
* 
* @param d
*            the double value to start
* @return the next larger double value of d.
* 
* @since 1.6
*/
public static double nextUp(double d) {
if (Double.isNaN(d)) {
return Double.NaN;
}
if ((d == Double.POSITIVE_INFINITY)) {
return Double.POSITIVE_INFINITY;
}
if (0 == d) {
return Double.MIN_VALUE;
} else if (0 < d) {
return Double.longBitsToDouble(Double.doubleToLongBits(d) + 1);
} else {
return Double.longBitsToDouble(Double.doubleToLongBits(d) - 1);
}
}
END OF UPDATE Ответ: Конечное количество (см. документацию Math.nextUp()). Для справки их 8 388 609 см. Java's new math, Part 2: Floating-point numbers http://www.ibm.com/developerworks/java/library/j-math2.html UPDATE 03-03-2011: Несколько типичных чисел в распечатке:
1.001352 1.0013521 1.0013522 1.0013523 1.0013524 1.0013525 1.0013527 1.0013528 1.0013529 1.001353
Если вас всё-таки интересует сколько есть чисел с двойной точностью (double) поменяется первую строчку на:
double x = 1.0D;
Напишите в комментариях, сколько чисел вы получили. :-) Несколько типичных чисел в распечатке:
1.000000000013522 1.0000000000135223 1.0000000000135225 1.0000000000135227 1.000000000013523
END OF UPDATE. Ну и напоследок серия вопросов на закрепление материала. :-) 3.
double d = Long.MIN_VALUE;
System.out.println(d+1==d);
Что будет распечатано? Подсказка
double d = Long.MIN_VALUE;
System.out.println(Math.ulp(d));
выдаст на экран 2048.0. Ответы будут даны ниже. 4.
double d = Long.MIN_VALUE;
System.out.println(d+513==d);
Что будет распечатано? 5.
double d = Long.MIN_VALUE;
System.out.println(d+512==d);
Что будет распечатано? Ответы: 3. true 4. false 5. true Плюс для справки Floating-point arithmetic http://www.javaworld.com/javaworld/jw-10-1996/jw-10-hood.html Floating-point arithmetic may give inaccurate results in Excel http://support.microsoft.com/kb/78113 http://en.wikipedia.org/wiki/Binary32 См. также О вреде округления Rounding error on Professional Entry Test Sample Questions Пи равно трём. Часть II. Почему в современных языках программирование нет типа данных для рациональных чисел?

1 comment:

  1. Почему для double d = Long.MIN_VALUE его значение меняется при добавлении 1/4 ULP, а не 1 ULP?

    ReplyDelete