Можно ли округлить 0.99999999999 до 1,0 при умножении? - PullRequest
36 голосов
/ 11 октября 2011

При умножении числа с плавающей запятой, которое очень близко к 1, на int> 0, его можно интерпретировать как 1.

То есть, если Math.random() возвращает максимально возможный результат (который на 1 шаг ниже 1,0),

(int)(Math.random() * 8)

быть 8 или 7?

В качестве практического примера, может ли эта часто используемая конструкция давать ошибку индекса за пределами границ:

someArray[(int)(Math.random() * someArray.length)];

Меня особенно интересуют ответы для Java и ActionScript 3, но я полагаю, что все они используют одни и те же правила для арифметики с плавающей запятой, и ответы для любой платформы будут полезны.

Обновление : хотя я уже принял ответ, я все равно был бы признателен за подтверждение того, что это не может пойти не так в ActionScript 3, поскольку коллега, сообщивший, что он видел, что он однажды ошибся, является тем, что частично побудил меня задать этот вопрос.

Ответы [ 2 ]

45 голосов
/ 11 октября 2011

Если умножить наибольшее значение ниже 1,0 на someInt (> 0), результат никогда не будет someInt.

Это может быть тщательно протестировано для целых чисел, как это:

Double greatestLessThanOne = Double.longBitsToDouble(4607182418800017407L);

// Assert that greatestLessThanOne is indeed the largest double less than 1.
//assert 1.0 == greatestLessThanOne + Math.ulp(greatestLessThanOne);

for (int i = 1; i >= 0; i++)
    if ((int) (greatestLessThanOne * i) == i)
        System.out.println("Exception found: " + i);

Фрагмент не выводит.

(Math.ulp возвращает расстояние между данным двойным и следующим двойным значением, большее по величине. Таким образом, утверждение гарантирует, что greatestLessThanOne действительно является наибольшим значением меньше 1,0.)

Другими словами, ваша строка

Object element = elementArray[(int)(Math.random() * elementArray.length)];

никогда не вызовет ArrayIndexOutOfBoundsException.


Кроме того, согласно комментарию Марка Дикинсона к здесь , это также относится к умножению на двойное число.

Используя арифметику IEEE 754 с плавающей запятой в режиме округления до ближайшего, вы можете показать, что x * y < y для любого x < 1.0 и любого немигающего положительного значения y. (Может произойти сбой, если y является либо ненормальным, либо наименьшим положительным нормальным числом.)

0 голосов
/ 11 октября 2011

Просто вокруг него, может быть так:

BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace,BigDecimal.ROUND_HALF_UP);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...