Почему возвращаемый по умолчанию тип «потолок» и «пол» является числовым?

Почему следующие все "numeric"?

class(ceiling(3))
class(ceiling(3L))
class(ceiling(3.1))
class(floor(2))
class(floor(2L))
class(floor(2.1))

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

Я попытался найти ответ, связанный с базовым кодом C, но на самом деле ничего не добился.

Я также узнал, что, хотя "%/%"(x,y) также всегда должно быть целым числом, class результатов зависит от типов ввода, например. 5%/%2, 6%/%2 и 6%/%2L все numeric, но 5L%/%2L и 6L%/%2L оба integer (кое-что об этом упоминается в ?Arithmetic); это тоже не имеет смысла для меня, но, по крайней мере, это задокументировано.

Есть ли простая причина для возврата объектов numeric из ceiling и floor? Если бы речь шла о неэффективности из-за повторного приведения (что может быть в случае целочисленного деления), я бы ожидал, что class(ceiling(3L)) будет "integer", так что же происходит?


person MichaelChirico    schedule 19.08.2015    source источник
comment
не уверен, что это удовлетворительное решение, но см. ?mode Режимы имеют тот же набор имен, что и типы (см. typeof), за исключением того, что типы integer и double возвращаются как числовые...   -  person Alex W    schedule 19.08.2015
comment
совершенно случайное предположение - может кто-то хотел иметь возможность сделать ceiling(Inf) и не получить в результате NA?   -  person eddi    schedule 19.08.2015
comment
@eddi, спасибо, не знал, что as.integer(Inf) был NA, так что, возможно, ты что-то понял...   -  person MichaelChirico    schedule 19.08.2015
comment
Потому что соответствующие функции C/C++ также возвращают значение типа double. А почему так? Вероятно, задействовать только блок процессора с плавающей запятой и избежать операции, т.е. целочисленного приведения, что не всегда необходимо (и если вам это нужно, очень просто добавить...)   -  person digEmAll    schedule 19.08.2015
comment
@digEmAll Я думал об этом, но для ceiling(3L) не должно быть приведения. Возможно, вы захотите уточнить, как работают C/C++ версии ceil, когда задано целое число.   -  person MichaelChirico    schedule 19.08.2015
comment
В случае целочисленного ввода, зачем вам вызывать ceil/floor для целочисленного значения? Наверняка разработчики R и C/C++ не позаботились о реализации бесполезной перегрузки, принимающей целое число и возвращающей себя...   -  person digEmAll    schedule 19.08.2015
comment
@digEmAll довольно легко представить случаи, когда вы бы сделали именно это, например. x<-list(1,1L); lapply(x,ceiling).   -  person MichaelChirico    schedule 19.08.2015
comment
@MichaelChirico: да, но в таких случаях я подозреваю, что в коде всегда есть какая-то проблема ... Я имею в виду, зачем вам называть потолок в списке, где вы знаете, что будут некоторые целые числа и некоторые двойные числа, и даже больше плохо, почему у вас есть список номеров разных типов? Как ты туда попал ? В R легко получить неожиданные типы (в конце концов, он плохо типизирован), но это не значит, что это хорошо, и обычно это запах кода IMO...   -  person digEmAll    schedule 19.08.2015
comment
@digEmAll это просто апостериорные оправдания - кого волнует, почему или как он туда попал. Суть в том, что нет ничего принципиально плохого в желании делать то, что он предлагает, и нет внутренней причины, по которой ceiling для целого числа должно возвращать число с плавающей запятой.   -  person eddi    schedule 20.08.2015
comment
@eddi: конечно, ничего страшного, но если вы начнете добавлять перегрузку к потолку/полу только для того, чтобы взять целое число и вернуть себя (если это не бесполезно это...), вы закончите создавать всякую бесполезность работает только потому, что никого не волнует, как он получил список целых чисел и плавает вместе ... ну, если вас это не волнует, то почему вас волнует этот потолок, который возвращает все числовые значения, а не целочисленные и числовые?   -  person digEmAll    schedule 20.08.2015
comment
@digEmAll Я, честно говоря, не понимаю вашу точку зрения - цель любой функции - обрабатывать ввод и производить вывод, а не для проверки намерений пользователя, если в них нет ничего изначально неправильного (например, если вы передали ему строку символов) . Эта функция не слишком хорошо работает с целыми числами, тем более что в справке она утверждается как универсальная.   -  person eddi    schedule 20.08.2015
comment
@digEmAll посмотрите ссылку, опубликованную Джораном в ответе Эдди ниже. Похоже, есть вполне разумное место, где нужна гибкость, о которой я говорю.   -  person MichaelChirico    schedule 20.08.2015
comment
@eddi: я в основном согласен с вашим утверждением, но, помимо моей точки зрения о полезности этих функций, я думаю, что за решением не реализовывать эти варианты есть и техническая причина. Общие функции AFAIK S3 не применяются к типам atomic (целочисленным, числовым, логическим и т. д.); поэтому вы не можете использовать общую систему диспетчеризации, но вам нужен какой-то оператор if внутри реализации по умолчанию, чтобы проверить, является ли он целым или двойным. Это добавляет условный переход (= потеря производительности) внутри функции, которая должна быть максимально быстрой (представьте, что вы используете ее миллионы раз...)   -  person digEmAll    schedule 20.08.2015
comment
@eddi: Итак, R-разработчики, вероятно, подумали, что если вам это действительно нужно, вы можете просто создать оболочку, например. ceilingIntAware <- function(x) if(is.integer(x))x else ceiling(x)   -  person digEmAll    schedule 20.08.2015
comment
@digEmAll, учитывая все остальное, что происходит в коде C для этой функции, я действительно не думаю, что дополнительный if будет иметь большое значение, и вы можете заставить компилятор выбирать числовую ветвь по умолчанию, чтобы не было скачка и в соответствии с потерей производительности кеша (и в случае происходит скачок, вы, по крайней мере, возвращаете цикл назад, так как вам не нужно ничего делать). Тем не менее, меня лично этот вопрос не волнует, и я не заинтересован в нем.   -  person eddi    schedule 20.08.2015
comment
@eddi: я в основном согласен с вами, вероятно, введение прыжка не приведет к потере производительности, в любом случае я все еще считаю, что эта модификация в основном бесполезна (или полезна в 0,01% случаев), поэтому разработчики не заинтересованы в его реализации. Тем не менее, меня это тоже не особо волнует, поэтому я не буду продолжать обсуждение и извините за такую ​​педантичность;)   -  person digEmAll    schedule 20.08.2015


Ответы (1)


Я не знаю, поэтому ли ceiling было разработано для возврата numeric, но в следующем примере показаны ограничения, которые были бы у ceiling, если бы он действительно возвращал целое число:

options(digits = 15)
.Machine$integer.max + 1.4
#[1] 2147483648.4

ceiling(.Machine$integer.max + 1.4)
#[1] 2147483649

as.integer(ceiling(.Machine$integer.max + 1.4))
#[1] NA
#Warning message:
#NAs introduced by coercion 

Тем не менее, нет веской причины, по которой я понимаю, почему ceiling не возвращает целое число при вводе целого числа.

person eddi    schedule 19.08.2015
comment
Несколько косвенно связано с это. - person joran; 20.08.2015
comment
@MichaelChirico: Что касается ссылки Джорана, да, seq кажется немного запутанным в отношении возвращаемых типов ... например, seq(1L,length.out=6L) возвращает числовое значение, а seq(1L,by=1L,length.out=6L) возвращает целое число ... при этом, вероятно, эту конкретную проблему можно решить, добавив перегрузку ceiling (и, конечно же, и другими способами...), но моя точка зрения заключается в том, что потолок/минимум для целых чисел в основном бесполезна (в том смысле, что вы можете получить то же самое, ничего не делая), и я не думаю, что R-разработчики интересует добавление ненужных функций... - person digEmAll; 20.08.2015
comment
Что касается причины, по которой ceiling(double) возвращает двойное значение, я согласен с eddi, что причиной может быть ограничение размера целого числа... - person digEmAll; 20.08.2015
comment
Это здорово, но не решает проблему для меня - кажется, эта проблема будет мешать любой целочисленной функции, и я не вижу особых причин заботиться об использовании ceiling/floor для массивных чисел - больше похоже на забота о round(x,-n) (в любом случае, как часто люди заботятся о цифре 1 в 10-значном числе?) - person MichaelChirico; 20.08.2015
comment
@MichaelChirico, это точно такое же заблуждение, как и то, что digEmAll говорил выше. Вы не разрабатываете функции, просто думая об использовании, которое вы сами можете придумать. Вы проектируете функции так, чтобы они могли делать все возможное в рамках любых внешних ограничений, которые у вас есть. - person eddi; 20.08.2015
comment
Я бы рассматривал использование памяти как внешнее ограничение, поэтому решение вернуть numeric вместо integer наталкивается на это, так что разработчики действительно должны принять это во внимание. В любом случае, я удивлен, что нет авторитетного источника по этой теме - действительно кажется, что выбор дизайна был сделан, в конце концов :\ - person MichaelChirico; 20.08.2015
comment
мда, это просто хватание за соломинку - я думаю, что потеря информации - гораздо более важная проблема; Кстати, я только что провел поиск в Google по этому вопросу - и, по-видимому, он появился практически на каждом языке, о котором вы только можете подумать, и ответ везде одинаков (выше) - person eddi; 20.08.2015
comment
интересно, так как первое, что я нашел о другом языке, было это, что я прочитал как противоположное... в любом случае, спасибо вам и @digEmAll за ваше время, я знаю, что это не слишком важно - я действительно ожидал, что будет простой ответ и авторитетный источник. - person MichaelChirico; 20.08.2015
comment
@MichaelChirico Я почти уверен, что целые и реальные числа имеют одинаковый размер в фортране - если это так, то было бы разумно возвращать целые числа - person eddi; 20.08.2015
comment
@MichaelChirico, относительно поведения Fortran: Fortran также предлагает функцию ieee_rint для округления до ближайшего целого числа (в соответствии с режимом округления) в типе данных IEEE вместо преобразования результата в целое число. - person francescalus; 15.05.2016
comment
Оглядываясь назад, я думаю, что это должен быть ответ. double вход, integer выход неизбежно приведут к ошибкам. Я считаю, что лучше заставить пользователя явно объявить, что ему нужно целое число. Даже на 100 000 000 элементов as.integer(floor(x)) занимает всего на 0,3 секунды больше, чем floor(x), поэтому затраты на эффективность минимальны. - person MichaelChirico; 15.05.2018