float (0.0) против 0.0f в CUDA

#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 .