# Conversions and Type Casting

## On this Page

# Conversions and Type Casting¶

## Implicit Conversions¶

Implicit conversions are supported between scalar built-in types that are defined in Built-in Scalar Data Types. When performing an implicit conversion, it is not only a re-interpretation of the expression’s value but also a conversion of that value to an equivalent value in the new type. For example, the integer value 5 is converted to the floating-point value 5.0.

Implicit conversions from a scalar type to a vector type are allowed. In this case, the scalar may be subject to the usual arithmetic conversion to the element type that the vector uses. The scalar type is then widened to the vector.

Implicit conversions between built-in vector data types are not allowed.

Implicit conversions for pointer types follow the rules described in the C99 specification.

See the examples below:

```
float f = 1.0f;
int i;
i = f; // allowed: i = 1
float fl = 1.234567f;
_Bfloat16 bf_16 = fl; // allowed
_Bool isOne = i; // allowed
int64 int_vec = 2; // allowed
uint64 uint_vec = int_vec; // not allowed
float64 fl_vec = 1.0f; // allowed
int_vec = fl_vec + 3; // not allowed
```

## Explicit Conversions (Casts)¶

Standard typecasts for built-in scalar data types, defined in Built-in Scalar Data Type, perform appropriate conversion. In the example below, fl stores 1.234567f and bf_16 stores 1.234f, which is the floating-point value 1.234567f in fl converted (reduced) to a _Bfloat16 value:

```
float f = 1.0f; // allowed
int i = (int)f; // allowed
float fl = 1.234567f; // allowed
_Bfloat16 bf_16 = (_Bfloat16) fl; // allowed
```

Scalar to vector conversions may be performed by casting the scalar to
the desired vector data type. Type casting also performs appropriate
arithmetic conversion. The round to zero rounding mode is used to
convert to built-in integer vector types. The default rounding mode
is used to convert to floating-point vector types. When
casting a `_Bool`

to a vector integer data type, the vector components
are set to 1 if the `_Bool`

value is *true*, and set to 0 otherwise.

Explicit casts between vector types are legal and can be performed in the usual way. See below more examples of explicit and implicit casts:

```
float f = 1.0f;
int i;
i = (int)f; // allowed
float64 fl_vec = 1.234567f; // allowed
int64 int_vec;
int_vec = (int_vec) fl_vec; // allowed
bfloat128 bf16_vec = (bfloat128) fl_vec; // allowed
```

```
_Bool isOne = 1; // allowed
int64 int_vec = isOne; // allowed
uint64 uint_vec = (uint64)int_vec; // allowed
```

```
float fl = 1.0f;
int64 int_vec;
int_vec = 2; //allowed
float64 fl_vec = 1.0f; // allowed
int_vec = fl + 3; // allowed
int_vec = (int64) fl_vec + 3; // allowed
```

## Explicit Conversions¶

Explicit conversions can be performed by the set of corresponding intrinscs. These provide a full set of type conversions between supported types (see Built-in Scalar, Built-in Vector and Other Built-in Data Types).

In general, a convert intrinsic has the following name:

```
s_convert_<sourceType>_to_<destType> // in case of scalar
v_convert_<sourceType>_to_<destType>_[<b/vb>] // in case of vector
```

Examples:

```
s_convert_i16_to_f32
v_convert_int16_to_i8_vb_b
```

The first *s* states the scalar conversion and v – for vector.

For the full set of available conversion intrinsics, refer to TPC Intrinsics Guide.

The behavior of the conversion may be modified by a set of switches that specify various conversion options, including rounding modes (see examples in Explicit Conversion Examples).

### Data Types¶

Conversions are available for the following scalar types: char, unsigned char, short, unsigned short, int, unsigned int, _Bfloat16, float, and built-in vector types derived from that. The operand and result type must have the same number of elements. The operand and result type may be the same type, in which case the conversion has no effect on the type or value of an expression.

Conversions between integer types follow the conversion rules specified
in *sections 6.3.1.1* and *6.3.1.3* of the C99 specification, except for
out-of-range behavior and saturated conversions, which are described in
Out-of-Range Behavior and Saturated Conversions
below.

### Rounding Modes¶

Conversions to and from floating-point types conform to IEEE-754 rounding rules. Conversions may have an optional rounding mode modifier, described in the table below:

Note

The default modifier value is zero which corresponds to the default round mode RHNE.

Modifier |
Rounding Mode Description |
---|---|

SW_RHNE |
Round to nearest even |

SW_RZ |
Round toward zero |

SW_RU |
Round toward positive infinity |

SW_RD |
Round toward negative infinity |

SW_SR |
Stochastic Rounding |

SW_RHAZ |
Round away from Zero (used for convert instruction) |

### Out-of-Range Behavior and Saturated Conversions¶

The conversion operand is said to be out-of-range when it is either greater than the greatest
representable destination value or less than the least representable
destination value. The conversion rules specified by the C99 specification
in *section 6.3.* determine the result of out-of-range conversion
when converting from a floating-point type to integer type other than `_Bool`

. The fractional
part is discarded (i.e., the value is truncated toward zero). The behavior is undefined when the
value of the integer type cannot represent the integer part.

Conversions to floating-point type conform to IEEE-754 rounding rules. When a value of integer type is converted to a real floating type, it is unchanged if the value being converted can be represented exactly in the new type. If the value being converted is in the range of values that can be represented but cannot be represented exactly, the result is either the nearest higher or nearest lower representable value, chosen according to the rounding mode in use. If the value being converted is outside the range of values that can be represented, the behavior is undefined.

The intrinsics implementing some operations on integer types (add, mac, sub) may use the optional saturated mode by setting the saturation modifier. When in saturated mode, values that are outside the representable range will clamp to the nearest representable value in the destination format. (NaN is converted to 0). The saturation modifiers may not be used for operations on floating-point formats. Listed below the available saturation modes:

Modifier |
Saturation Mode Description |
---|---|

SW_SAT |
Use saturation in conversion |

```
int64 arg1, arg2, sum;
uint64 sum = v_i32_add_b(arg1, arg2, SW_SAT);
// sum[i] = MaxInt for all i: arg1[i] + arg2[i] > MaxInt
* by default no saturation is apply on instructions.
```

### Explicit Conversion Examples¶

The below are examples of different use cases:

```
float64 f = -3.5;
...
int64 i = v_convert_f32_to_i32_vb(f ,SW_RHNE ,i);
// all elements of i = -4
```

```
char256 ch256;
...
uint64 u64 = v_convert_i8_to_u32_vb(ch256);
```

## Reinterpreting Data as Another Type¶

It is frequently necessary to reinterpret bits in a data type as another data type in TPC C. This is typically required when direct access to the bits in a floating-point type is needed, such as to mask off the sign bit, or make use of the result of a vector relational operator on floating-point data (see Explicit Conversions.).

### Reinterpreting Types Using Unions¶

The TPC-C language extends the union, to allow the program to access a member of a union object using a member of a different type. The relevant bytes of the representation of the object are treated as an object of the type used for the access. As stated in Structures and Unions, the data types of union object members cannot be from different memory classes. See the examples below:

```
union Vec_values { // allowed
int64 int_vec;
float64 float_vec;
```

union Vec_Sc_values { // not allowed

float element_0;

float64 float_vec;

union Index_array { // not allowed

int5 indexes;

int64 int_vec;

index Index_array { // allowed

int5 indexes;

int depth

### Reinterpreting Types Using **as_type**() Operator¶

Scalar and vector data types described in Built-in Scalar Data Types
and Built-in Aggregate Data Types (except `_Bool`

) may be also reinterpreted as
another data type of the same size using the **as_type()** construct
both for scalar and vector data types. The bits in the operand are
returned directly without modification as the new type. Byte order is
little-endian. The usual type promotion for function arguments is not
performed. It is an error to use **as_type**() operator to reinterpret
data to a type with a different number of bytes. See the examples below:

##
Examples:

```
int5 coords;
...
int s0 = 0x3f800000;
float f = as_float(s0); // allowed, f contains 1.0f
float64 v0 = v_f32_ld_tnsr_i(coords, src);
v0 += as_float(s0); // allowed, adds 1.f to each component
char256 v1 = as_char256(v0); // allowed
v1 += as_char(s0); // not allowed, size mismatch
short128 v2 = as_short128(v1); // allowed
v2 += (short)as_float(coords[0]); // allowed
i16_st_tnsr_i_v(coords,dst, v2);
...
v0 = -1.0f;
int64 i64 = as_int64(v0); // allowed, i64 contains 0xbf800000 in each
component
```

## Pointer Casting¶

Pointers to standard C99 types and new types may be cast back and forth to each other. Casting a pointer to a new type represents an unchecked assertion that the address is correctly aligned. Cast between pointers in different address spaces is not allowed.

The developer must also know the endianness of the TPC device and the endianness of the data, to determine how the scalar and vector data elements are stored in memory.

## Usual Arithmetic Conversions¶

The general rules of arithmetic type conversions to use for scalar
expressions are described in *Section 6.3.1.8* of the C99 Language
Specification. The following rules apply for arithmetic expressions
with vector data types.

All vector types are considered with higher conversion ranks than scalars. If there is only a single vector type, and all other operands are scalar types. The scalar types are converted to the type of the vector element, then widened into a new vector containing the same number of elements as the vector by duplicating the scalar value across the width of the new vector. An error occurs when the operands are of more than one vector type. Implicit conversions between vector types are not permitted, per Implicit Conversions.

An error occurs if any scalar operand has greater rank than the type of the vector element. For this purpose, the following rank order is defined:

The rank of a floating-point type is greater than the rank of another floating-point type, if the first floating-point type can exactly represent all numeric values in the second floating-point type. (For this purpose, the encoding of the floating-point value is used, rather than the subset of the encoding usable by the device.)

The rank of any floating-point type is greater than the rank of any integer type.

The rank of an integer type is greater than the rank of an integer type with less precision.

The rank of an unsigned integer type is

**greater than**the rank of a signed integer type with the same precision. (This is different from the standard integer conversion rank described in C99 TC2,*section 6.3.1.1*)The rank of the _Bool type is less than the rank of any other type.

The rank of an enumerated type is equal to the rank of the compatible integer type.

For all types, T1, T2 and T3, if T1 has greater rank than T2, and T2 has greater rank than T3, then T1 has greater rank than T3.

Otherwise, if all operands are scalar, the usual arithmetic conversions
apply, per *section 6.3.1.8* of the C99 standard.