#c #cuda #floating-point #double #precision
#c #cuda #с плавающей запятой #двойной #точность
Вопрос:
Я хочу написать код, действительный как для float, так и для double точности. Я делаю что-то вроде этого:
typedef real float;
//typedef real double;
__global__ void foo(real a, real *b){
b[0] = real(0.5)*a;
}
int main(){
real a = 1.0f;
real *b;
cudaMalloc(amp;f, sizeof(real));
foo<<<1,1>>>(a,b);
return 0;
}
Это заставило меня задуматься, я не хочу терять точность в константе как 0.5f при выполнении двойной точности, но я не хочу увеличивать 0.5 до double при выполнении одиночной точности!
В итоге я использовал оператор real(), как в примере. В режиме одиночной точности, если я разбираю функцию ‘foo’, используя real (0.5), я получаю, что для удвоения нет повышения, в отличие от использования только 0.5, где происходит повышение.
Вы можете проверить с помощью:
$nvcc test.cu -arch=sm_52 -lineinfo --source-in-ptx -g -G -O0 ; cuobjdump -sass a.out | grep "foo" -A 35
Я вижу
/*0078*/ FMUL R0, R0, 0.5; /* 0x3868004000070000 */
При использовании real(0.5) или 0.5f
и:
/*0078*/ F2F.F64.F32 R4, R0; /* 0x5ca8000000070b04 */
/*0088*/ DMUL R4, R4, 0.5; /* 0x3880004000070404 */
/*0090*/ F2F.F32.F64 R0, R4; /* 0x5ca8000000470e00 */
При записи всего 0.5.
Это может показаться слишком очевидным. Но поскольку я не знаю, что делает «real (0.5)», я не могу знать, просто ли это компилятор, играющий в этом конкретном случае. Дизассемблированный код кажется идентичным как в реальном (0.5), так и в 0.5f!
Итак, вопрос остается:
Что именно делает real (0.5) (он ЖЕ float(0.5))?
Есть ли разница между float (0.5) и 0.5f? (ИЛИ double (0.5) и 0.5)
Я полагаю, это относится и к C / C .
Ответ №1:
Что именно делает real (0.5) (он ЖЕ float(0.5))?
real(0.5)
приведение в функциональном стиле, и в этом случае оно уменьшается до static_cast
real(0.5)
static_cast<real>(0.5) //exactly the same thing
Это означает a
, что умножается на real
переменную (в данном случае float
), что означает, что нет необходимости выполнять продвижение на double
, как это было бы в случае double * float
умножения.
Есть ли разница между float (0.5) и 0.5f? (ИЛИ double (0.5) и 0.5)
Можно утверждать, что инициализация float
with 0.5
может произойти во время выполнения, но это нереально для любого современного компилятора. Это должно быть no-op, это уже для OP.
Кроме этого, использование float(0.5f)
is не имеет никакого значения по сравнению с простым использованием 0.5f
, и то же самое относится и к double(0.5)
and 0.5
.
Комментарии:
1. Из ссылки: 1) Если существует неявная последовательность преобразования из выражения в new_type или если разрешение перегрузки для прямой инициализации объекта или ссылки типа new_type из выражения найдет хотя бы одну жизнеспособную функцию, тогда static_cast<new_type>(выражение) возвращает мнимую переменную Temp, инициализированную так, как если быс помощью new_type Temp(выражение);, которое может включать неявные преобразования, вызов конструктора new_type или вызов определяемого пользователем оператора преобразования.
2. Не означает ли это, что static_cast<real>(0.5) вызывает real(0.5)? Кроме того, «[static_cast] может включать неявные преобразования, вызов конструктора new_type или вызов определяемого пользователем оператора преобразования.». Значит, это что-то делает во время выполнения, а не 0,5f?
3. Если бы у вас там были более сложные типы (с конструктором), то это могло бы быть. Однако в вашем случае вы можете легко увидеть, что ничего подобного не происходит. Вы инициализируете
real
значение0.5
. Компилятор знает, как это сделать во время компиляции, и тогда он не вынужден генерировать повышение в double для выполнения умножения.4. Другими словами, float(0.5) и 0.5f должны генерировать точно такой же код?
5. @Apo Я не думаю, что это гарантировано . Но если бы был компилятор, достаточно тупой, чтобы не выполнять такую тривиальную оптимизацию, вы не должны его использовать. Я уверен, что это безопасно, всегда будет правильным для вас, чтобы использовать
real(0.5)
здесь, чтобы устранить неоднозначность междуfloat
иdouble
.