Подтвердить что ты не робот

Почему Math.Round/Floor/Ceiling не возвращаются долго или int?

Каждый раз, когда я использую Math.Round/Floor/Ceiling, я всегда прикладываю к int (или, возможно, long при необходимости). Почему именно они возвращают double, если он всегда возвращает целое число.

4b9b3361

Ответ 1

Результат может не соответствовать int (или длинному). Диапазон двойника намного больше.

Приблизительный диапазон двойных: ± 5,0 × 10 -324 до ± 1,7 × 10 308

(Источник)

Ответ 2

Я согласен с ответом Марка на то, что результат может не совпадать с long, но вы можете подумать: что, если у С# был гораздо длиннее long? Ну, вот что происходит в Python с его целыми целыми строками:

>>> round(1.23e45)
1229999999999999973814869011019624571608236032

Большая часть цифр - это "шум" от ошибки округления с плавающей запятой. Возможно, частью мотивации для Round/Floor/Ceiling возврата double в С# было избежать иллюзии ложной точности.

Альтернативное объяснение заключается в том, что модуль .NET Math использует код, написанный на C, в котором floor и ceil возвращает типы с плавающей точкой.

Ответ 3

Аргументы диапазона в сторону, ни один из этих ответов не касается того, что для меня представляет собой фундаментальную проблему с возвратом числа с плавающей точкой, когда вы действительно хотите точное целое число. Мне кажется, что вычисленное число с плавающей запятой может быть меньше или больше требуемого целого числа с помощью небольшой ошибки округления, поэтому операция литья может создать отключение на одну ошибку. Я бы подумал, что вместо кастинга вам нужно применить целочисленную (не двойную) округло-ближайшую функцию к двойному результату floor(). Или напишите свой собственный код. В любом случае версии библиотеки C floor() и ceil() очень медленные.

Это правда, или я чего-то не хватает? Есть что-то о точном представлении целых чисел в стандарте с плавающей точкой IEEE, но я не уверен, делает ли это безопасным литье.

Я бы предпочел проверить диапазон в функции (если это необходимо, чтобы избежать переполнения) и вернуть длинный. Для моего личного кода я могу пропустить проверку диапазона. Я делаю это:

long int_floor(double x)
{
    double remainder;
    long truncate;
    truncate = (long) x;        // rounds down if + x, up if negative x
    remainder = x - truncate;   // normally + for + x, - for - x
    //....Adjust down (toward -infinity) for negative x, negative remainder
    if (remainder < 0 && x < 0)
        return truncate - 1;
    else
        return truncate;
}

Соотношения существуют для ceil() и round() с разными соображениями для отрицательных и положительных чисел.

Ответ 4

В документах, которые я мог найти, нет причин. Лучше всего предположить, что если вы работаете с удвоениями, скорее всего, вы хотите, чтобы любые операции над двойниками возвращали двойной. Округление его на приведение к int было признано разработчиком языка менее обычным, чем округление и сохранение как двойное.

Вы можете написать свой собственный метод, который передал бы его int для вас примерно в 2 строках кода и гораздо меньше работал, чем опубликовал вопрос о переполнении стека...