OpenCL编程指南-4.3类型转换

news2024/12/30 3:48:33

隐式类型转换

隐式类型转换是一种自动的类型转换,只要混合使用不同的类型,编译器就会完成这种隐式类型转换。这里支持表4-1中定义的标量类型(除voiddoublehalf以外)的隐式转换。完成隐式转换时,并不只是重新解释一个表达式的值,而是将这个值转换为新类型的一个等价的值。

考虑下面的例子:

float f = 3; //implicit conversion to float value 3.0
int i = 5.23f; //implicit conversion to float value 5

在这个例子中,值3转换为float3.0f,然后赋予f。值5.23f转换为一个int值5,然后赋予i。在第二个例子中,float值的小数部分被丢弃,因为整数不支持小数值,这是一个不安全的类型转换例子。

指针类型的隐式转换也遵循C99规范中描述的规则。不允许内置矢量数据类型之间进行隐式转换。例如:

float4 f;
int4 i;
f = i; //illegal implicit conversion between vector data types

常规算术转换

有些需要算术类型(整数或浮点类型)操作数的操作符可能会带来转换,并以类似的方式得到结果类型。我们的目的是为操作数和结果确定一个通用的真实类型。对于指定的操作数,各操作数会转换(不改变类型域)为另外一个类型,其相应的真实类型为通用真实类型。为此,我们认为所有矢量类型比标量有更高的转换等级。除非另外明确说明,否则一般来讲通用真实类型也是结果的相应真实类型,如果操作数相同而且很复杂,那么结果的类型域就是操作数的类型域。这种模式称为常规算术转换( usual arithmetic conversion)。

如果操作数是多个矢量类型,则会出现一个编译时错误。矢量类型之间的隐式转换是不允许的;否则,如果只有一个矢量类型,而且所有其他操作数都是标量类型,那么这些标量类型会转换为矢量元素的类型,再宽化为一个新矢量,其中包含原矢量相同数目的元素,这里会把标量值全面复制到这个新矢量。如果某个标量操作数的等级大于矢量元素的类型,就会产生一个编译时错误。对此,等级顺序定义如下:
1)如果一个浮点类型可以准确地表示另一个浮点类型中的所有数值,那么第一个浮点类型的等级大于第二个浮点类型(对此,使用浮点值编码,而不是设备可用编码的子集)。
2)任何浮点类型的等级都大于整数类型的等级。
3)整数类型的等级大于精度较低的整数类型的等级。
4)无符号整数类型的等级大于有相同精度的、有符号整数类型的等级。
5)bool的等级小于所有其他类型。
6)枚举类型的等级等于兼容的整数类型的等级。
7)对于所有类型T1、T2和T3,如果T1的等级大于T2,而且T2的等级大于T3,则T1的等级大于T3。

否则,如果所有操作数都是标量,则按C99规范中定义应用常规算术转换。下面是矢量以及矢量和标量操作数合法使用算术转换的一些例子:

short a;
int4 b;
int4 c = b + a;

在这个例子中,变量a(类型为short)转换为int4,然后完成矢量加法。

int a;
float4 b;
float4 c = b + a;

在前面的例子中,变量a(类型为int)转换为float4,然后完成矢量加法。

float4 a;
float4 b;
float4 c = b + a;

在这个例子中,不需要完成任何转换,因为a、b和c都是相同的类型。下面是对矢量以及矢量和标量操作数非法使用常规算术转换的几个例子:

int a;
short4 b;
short4 c = b + a; //cannot convert & widen int to short4

double a;
float4 b;
float4 c = b + a; //cannot convert & widen double to float4

int4 a;
float4 b;
float4 c = b + a; //cannot cast between different vector types

显式强制类型转换

表4-1中定义的标量数据类型的标准类型强制类型转换会完成适当的变换(除了voidhalf以外)。在下面的例子中,f存储0x3F800000i存储0x1,这就是转换f中的浮点值1.0f得到的整数值:

float f = 1.0f;
int i = (int)f;

矢量类型之间的显式强制类型转换是不合法的。下面的例子会生成一个编译错误:

int4 i;
uint4 u = (uint4)i; //compile error

float4 f;
int4 i = (int4)f; //compile error

float4 f;
int8 i = (int8)f; //compile error

完成标量到矢量的转换后,会把标量强制转换为所需要的矢量数据类型。强制类型转换还会完成适当的算术转换。转换为内置整数矢量类型时会采用向零舍入模式。转换为内置浮点矢量类型时,则采用就近舍入模式。将bool强制转换为整数矢量类型时,如果这个bool值为true,则矢量分量设置为-1(也就是说,设置所有位为1);否则,设置为0

下面是显式强制类型转换的一些例子:

float4 f = 1.0f;
float4 va = (float4)f; //va is a float4 vector with elements(f,f,f,f)

uchar u = 0xFF;
float4 vb = (float4)u; //vb is a float4 vector with elements ((float)u,(float)u,(float)u,(float)u)

float f = 2.0f;
int2 vc = (int2)f; //vc is an int2 vector with elements ((int)f,(int)f)

uchar4 vtrue = (uchar4)true; //vtrue is a uchar4 vector with elements(0xFF,0xFF,0xFF,0xFF)

显式转换

不允许在矢量类型之间完成隐式转换和显式强制类型转换。不过,有些情况下我们可能需要将一个矢量类型转换为另一个类型。另外,可能有必要指定完成转换时使用的舍入模式,以及转换的结果是否饱和。这对于标量和矢量数据类型都很有用。

考虑下面的例子:

float x;
int i = (int)x;

在这个例子中,x中的值截断为一个整数值,并存储在i中。也就是说,将浮点值转换为一个整数值时,强制类型转换会完成向零舍入。

有时,我们需要将浮点值舍人为最接近的整数。下面的例子展示了通常如何做到这一点:

float x;
int i = (int)(x + 0.5f);

对于大多数x值,这都能正确工作,除非x0.5f -1 ulp或者x是一个负数。x0.5f -1 ulp时,(int)(x + 0.5f)返回1。也就是说,它会向上舍入而不是向下舍入。x是一个负数时,(int) (x + 0.5f)会向下舍入而不是向上舍入。

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <float.h>

int main(void)
{
	float a = 0.5f;
	float b = a - nextafterf(a, (float)-INFINITY); // a - 1 ulp
	printf("a = %8x, b = %8x\n", *(unsigned int*)&a, *(unsigned int*)&b);
	printf(" (int) (a + 0.5f) = %d \n", (int)(a + 0.5f));
	printf(" (int) (b + 0.5f) = %d \n", (int)(b + 0.5f));
}

在这里插入图片描述
可以增加适当的检查来查看x的值,然后完成正确的转换,这样做可以修正这些问题,不过还有一些硬件能够根据大多数设备上的舍入和饱和设置完成这些转换。从性能的角度来看,这很重要:OpenCL C允许开发人员使用适当的硬件ISA完成这些转换而不是借助于在软件中模拟。正是因为这个原因,OpenCL实现了一些内置函数,可以基于一些选项(选择饱和度和4种舍入模式之一)完成一种类型到另一种类型的转换。

可以使用以下函数完成显式转换:

destType convert_destType><_sat><_roundingMode>(sourceType)
destType convert_destTypen><_sat><_roundingMode>(sourceTypen)

这些函数为标量类型(char、uchar、short、ushort、int、uint、long、ulong、float、double、half)和由这些标量类型衍生的内置矢量类型提供了各种转换。操作数和结果类型必须有相同数目的元素。操作数和结果类型可能是相同的类型,在这种情况下,转换对类型或值没有任何影响。

在下面的例子中,convert_int4将一个uchar4矢量u转换为int4矢量c

uchart4 u;
int4 c = convert_int4(u);

在下一个例子中,convert_int将一个float标量f转换为int标量i

float f;
int i = convert_int(f);

舍入模式修饰符可选,如下所示。

_rte            舍入为最近的偶数
_rtz            向零舍入
_rtp            向正无穷大舍入
_rtn            向负无穷大舍入
未指定修饰符     使用这个目标类型的默认舍入模式:转换为整数时为_rtz,转换为浮点类型时为_rte

可选的饱和度修饰符(_sat)可以用来指定转换的结果必须饱和至结果类型。转换操作数大于可表示的最大目标值或者小于可表示的最小目标值时,称之为越界。在整数类型之间转换时,对于越界的输入,其结果值将等于对应于相应目标元素的源操作数元素中的最低有效位集合。从一个浮点类型转换为一个整数类型时,其行为由具体实现定义。

转换为整数类型时,最好使用可选的饱和模式完成转换,即在转换函数名后面追加_sat修饰符。在饱和模式中,超出可表示范围的值会强制为采用目标格式的最近的可表示值(NaN要转换为0)。

转换为浮点类型时,要遵循IEEE754舍入规则。_sat修饰符不能用于转换为浮点格式。

下面是几个使用显式转换函数的例子。

下面的例子显示将一个float4转换为ush-ort4,这里指定了就近舍入模式和饱和度。图4-2描述了f中的值和c中的转换结果。
在这里插入图片描述

float4 f = (float4)(-5.0f, 254.5f, 254.6f, 1.2e9f);
ushort4 c = convert_uchar4_sat_rte(f);

下面的例子描述了将一个有符号值转换为无符号值或者对整数类型完成向下转换时,饱和度修饰符的行为:

short4 s;

//negative values clamped to 0
ushort4 u = convert_ushort4_sat(s);

//values > CHAR_MAX converted to CHAR_MAX
//values < CHAR_MIN converted to CHAR_MIN
char4 c = convert_char4_sat(s);

下面的例子展示了指定饱和度和舍入模式修饰符时,将一个浮点值转换为整数值:

float4 f;
//value implementation-defined for f > INT_MAX, f < INT_MAX, or NaN
int4 i = convert_int4(f);
//values > INT_MAX clamp to INT_MAX
//values < INT_MIN clamp to INT_MIN
//NaN should produce 0
//The _rtz rounding mode is used to produce the integer values
int4 i2 = convert_int4_sat(f);
//similar to convert_int4 except that floating-point values
//are rounded to the nearest integer instead of truncated
int4 i3 = convert_int4_rte(f);
//similar to convert_int4_sat except that floating-point values
//are rounded to the nearest integer instead of truncated
int4 i4 = convert_int4_sat_rte(f);

最后一个转换例子显示了有可选的舍人模式修饰符和无舍入模式修饰符的情况下,将一个整数值转换为浮点值:

int4 i;
//convert ints to floats using the round-to-nearest rounding mode
float4 f = convert_float4(i);
//convert ints to floats; integer values that cannot be
//exactly represented as floats should round up to the next
//representable float
float4 f = convert_float4_rtp(i);

将数据重新解释为另一种类型

在有些情况下,你可能希望屏蔽一个浮点类型的符号位。在C中解决这个问题有很多方法,可以使用指针别名或联合,或者使用memcpy。其中,只有memcpy在C99中是严格正确的。由于OpenCL C不支持memcpy,所以我们需要一种不同的方法来完成这种屏蔽操作。我们需要的一般功能是能够将一个数据类型中的位重新解释为另一种数据类型。在前面的例子中,即希望屏蔽一个浮点类型的符号位,我们希望将这些位重新解释为一个无符号整数类型,然后屏蔽符号位。还有另外一些例子,比如使用一个矢量关系操作符的结果,以及抽取一个浮点类型的指数或尾数位。

as_typeas_typen内置函数允许将一个数据类型的位重新解释为另一个相同大小的数据类型。as_type用于标量数据类型(除boolvoid以外),as_typen则用于矢量数据类型。只有当实现支持适当的扩展时,才支持doublehalf

下面的例子描述了如何使用as_type 内置函数屏蔽一个浮点类型的符号位:

float f;
uint u;

u = as_uint(f);
f = as_float(u & ~1 << 31));

如果操作数和结果类型包含相同数目的元素,那么操作数中的位直接返回,不会修改为新类型;如果操作数和结果类型包含的元素个数不同,则会出现两种情况:
1)操作数是一个包含4个分量的矢量;结果是一个包含3个分量的矢量。在这种情况下,操作数的xyz分量与结果有相同的位。结果的w分量则认为未定义。
2)对于所有其他情况,具体行为由实现定义。

下面给出几个例子展示如何使用as_typeas_typen。下一个例子显示了如何将一个int重新解释为一个float

uint u = 0x3f800000;
float f = as_float(u);

变量u(声明为一个无符号整数)包含值0x3f800000。这表示单精度浮点值1.0。变量f现在包含浮点值1.0

下面的例子中,将一个float4重新解释为一个int4 :

float4 f = (float4)(1.0f, 2.0f, 3.0f, 4.0f);
int4 i = as_int4(f);

变量i(类型为int4)在其xyzw分量中有以下值:0x3f8000000x400000000x404000000x40800000

下一个例子展示了如何使用as_typen对浮点矢量类型执行三元选择操作符(?:):

//Perform the operation f = f < g ? f:0 for components of a vector
float4 f, g;
int4 is_less = f < g;

//Each component of the is_less vector will be 0 if result of <
//operation is false and will be -1 (i.e.., all bits set) if
//the result of < operation is true
f = as_float4(as_int4(f) & is_less);
//This basically selects f or 0 depending on the values in is_less..

下面的例子展示了操作数和结果中元素个数不同的情况,这里as_typeas_typen的行为由具体实现定义:

int i;
short2 j = as_short2(i); //Legal, Result is implementation-defined
int4 i;
short8 j = as_short8(i); //Legal, Result is implementation-defined
float4 f;
float3 g = as_float3(f); //Legal, g.xyz will have same values as f.xyz, g.w is undefined

这个例子展示了将一个4分量的矢量重新解释为3分量矢量:

float4 f;
float3 g = as_float3(f); //Legal, g.xyz will have same values as f.xyz, g.w is undefined

下一个例子显示了非法使用as_typeas_typen的情况,这会得到编译错误:

float4 f;
double4 g = as_double4(f);
float3 f;
float4 g = as_float4(f);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/530442.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c/c++中的数据内存分布

c/c中的数据内存分布 1. C/C内存分布2. C语言中动态内存管理方式3. C中动态内存管理4. operator new与operator delete函数5. new和delete的实现原理6. malloc/free和new/delete的区别7.内存泄漏**什么是内存泄漏&#xff0c;内存泄漏的危害****内存泄漏分类&#xff08;了解&a…

练习时长两年半的扫雷

目录 设计思路 游戏运行效果 函数的声明 头文件game.h 游戏主体(源文件) 1.game.c 2.test.c 各文件的阐述 各部分设计心得 1.打印菜单 2.初始化雷池 3.打印雷池以及玩家界面 打印效果 如何改变雷的数量与雷池大小 4.生成随机雷 5.排雷与对局判断 对于越界的看法 设计…

Pruning 系列 (五)Dropout、Dropconnect剪枝

环境 python 3.9numpy 1.24.1pytorch 2.0.0+cu117一、Dropout dropout在全连接层使用。 假设丢弃概率是P,有两种实现方式,不管是在《测试阶段》还是《训练阶段》对每层的输出做缩放,都是为了保持伯努利分布的期望np。《测试阶段》没dropout 而是全量神经元参与运算,不然预…

JAVA8的新特性——lambda表达式

JAVA8的新特性——lambda表达式 此处&#xff0c;我们首先对于Java8的一些特性作为一个简单介绍 Java 8是Java编程语言的一个重要版本&#xff0c;于2014年发布。Java 8引入了许多新特性和改进&#xff0c;以提高开发效率和性能。以下是Java 8的一些主要新特性&#xff1a; Lam…

Liunx基础命令 - pwd命令

pwd命令 – 显示当前工作目录的路径 ​ pwd 命令来自英文词组“print working directory”的缩写&#xff0c;其功能是用于显示当前工作目录的路径&#xff0c;即显示所在位置的绝对路径。在实际工作中&#xff0c;我们经常会在不同目录之间进行切换&#xff0c;为了防止“迷路…

【Android】Exam5 ListView组件简单应用

Exam5 ListView组件简单应用 ListView组件简单应用 Exam5 ListView组件简单应用目的实验内容及实验步骤采用SimpleAdapter自定义Adapter运行及结果&#xff1a;实验总结 目的 掌握常用的UI布局及组件&#xff1b; 掌握使用Intent启动Activity的方法 掌握ListView组件的简单应用…

什么是微服务中的熔断器设计模式?

在本文中&#xff0c;我将解释什么是熔断器设计模式以及它解决了什么问题。 我们将仔细研究熔断器设计模式&#xff0c;并探讨如何使用Spring Cloud Netflix Hystrix在Java中实现它。到本文结束时&#xff0c;您将更好地了解如何使用熔断器设计模式提高微服务架构的弹性。 熔断…

创建基于oracle jdk8的自定义docker镜像

创建基于oracle jdk8的自定义docker镜像 1:查看服务器java版本&#xff1a; 如果服务器的版本是open-jdk&#xff0c;则进行如下操作 拷贝相关jdk压缩包&#xff08;.tar.gz后缀&#xff09;到服务器目录&#xff08;例&#xff1a;/usr/local&#xff09; 解压&#xff1a;…

Liunx基础命令 - ls命令

ls命令 – 显示目录中文件及其属性信息 ls命令来自英文单词”list“的缩写&#xff0c;中文译为“列出”&#xff0c;其功能是用于显示目录中文件及其属性信息&#xff0c;是最常被使用到的Linux命令之一。 默认不添加任何参数的情况下&#xff0c;ls命令会列出当前工作目录中的…

Servlet编程---Day 04

一、HttpServletRequest (请求对象) &#xff08;一&#xff09;HttpServletRequest对象 HttpServletRequest对象是 tomcat 为我们封装的对象 HttpServletRequest是 ServletRequest 接口的子接口 , 专门做 http 协议的请求对象 &#xff08;二&#xff09;常用方法 //设置…

Python3: 扫描库文件并获取版本号信息

文章目录 1. 目的2. 原理Linux: strings 命令Windows: strings 命令 3. 基于 Python 实现 strings 命令4. 基于Python的版本号查找5. 最终调用&#xff1a;一句话使用 1. 目的 在 C/C 开发中使用了第三方库&#xff0c;具体说是 .a, .lib, .dll 等文件&#xff0c;想通过 Pyth…

C++: 通过CMake配置AddressSanitizer并执行内存泄漏和越界检查

文章目录 1. 目的2. 区分编译和链接选项3. 在CMake中全局开启ASan1. 目的 在 C/C++ 工程中, 得益于 Google 工程师开发的 Address Sanitizer 这一神器, 可以快速、准确的发现不规范的内存使用, 包括而不限于: 内存泄漏检查, 例如忘记释放, 或原本持有内存的的指针被赋予…

[230522] 托福阅读词汇题 |持续更新|5月15日

infiniteimmensevast breakthroughdiscovery pivotalessential perceivedapparent 明显的 statutorilylegally 法律上 triggerprompt 引发 adolescentyouthful 青少年的 theorizedproposed 提出 replenishrenew 补充 prospersucceed pursueresearch signalindicate …

WPF MaterialDesign 初学项目实战(4)侧边栏路由管理

原视频内容 WPF项目实战合集(2022终结版) 24P 其他内容 WPF MaterialDesign 初学项目实战&#xff08;0&#xff09;:github 项目Demo运行 WPF MaterialDesign 初学项目实战&#xff08;1&#xff09;首页搭建 WPF MaterialDesign 初学项目实战&#xff08;2&#xff09;首…

ESLint配置详解

ESLint配置详解 ESLint 是一个代码检查工具&#xff0c;用来检查代码是否符合指定的规范&#xff0c;防止在多人协作开发时代码格式不统一。 安装 全局安装 npm install eslint -g当前项目安装 npm install eslint -D安装之后运行eslint --init进行初始化&#xff0c;使用…

你真的理解分布式数据的分区吗?

分布式数据存储是指将数据分散存储在多个节点或服务器上的技术。而分区是将数据划分为逻辑上的片段或部分&#xff0c;每个分区可以在分布式系统中的不同节点上存储。分区主要是为了可扩展性。不同的分区可以放在不共享集群中的不同节点上&#xff0c;可以帮助实现负载均衡、高…

玩转Google开源C++单元测试框架Google Test系列(gtest)之四 - 参数化

一、前言 在设计测试案例时&#xff0c;经常需要考虑给被测函数传入不同的值的情况。我们之前的做法通常是写一个通用方法&#xff0c;然后编写在测试案例调用它。即使使用了通用方法&#xff0c;这样的工作也是有很多重复性的&#xff0c;程序员都懒&#xff0c;都希望能够少…

Liunx基础命令 - cd命令

cd命令 – 切换目录 cd命令来自英文词组“change directory”的缩写&#xff0c;其功能是用于更改当前所处的工作目录&#xff0c;路径可以是绝对路径&#xff0c;也可以是相对路径&#xff0c;若省略不写则会跳转至当前使用者的家目录。 **语法格式&#xff1a;**cd [参数] …

VMware Workstation 17 Pro安装配置CentOS 7与ssh工具链接配置

VMware Workstation 17 Pro安装配置CentOS 7与ssh工具链接配置 下载安装虚拟机VMware Workstation 17 Pro 虚拟机官网&#xff1a;点击直达 下载Cent os 7 镜像文件 123网盘地址&#xff1a;点击直达 提取码1213 在虚拟机中安装Cent os 7 第一步 点击 创建新的虚拟机 第二步 默…

解释什么是蓝绿发布?

蓝绿发布(Blue-green release)是一种软件部署策略&#xff0c;主要用于应对新版本软件在生产环境中的测试和部署。这种策略将新版本软件分为两个阶段&#xff1a;蓝色阶段和绿色阶段。蓝色阶段通常在开发和测试环境中进行&#xff0c;而绿色阶段则在生产环境中进行。 蓝色阶段…