Каждый раз, когда я использую Math.Round/Floor/Ceiling
, я всегда прикладываю к int
(или, возможно, long
при необходимости). Почему именно они возвращают double
, если он всегда возвращает целое число.
Почему Math.Round/Floor/Ceiling не возвращаются долго или int?
Ответ 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 строках кода и гораздо меньше работал, чем опубликовал вопрос о переполнении стека...