Lably
Skip to content

Type conversion

Changing an expression from one data type to another

  • 13 min read
  • 4 chapters
  • 1 lab on Lably

Learn it on Lably

Each programming language has its own rules on how types can be converted. Languages with strong typing typically do little implicit conversion and discourage the reinterpretation of representations, while languages with weak typing perform many implicit conversions between data types. Weak typing language often allow forcing the compiler to arbitrarily interpret a data item as having different representations—this can be a non-obvious programming error, or a technical method to directly deal with underlying hardware.

In most languages, the word coercion is used to denote an implicit conversion, either during compilation or during run time. For example, in an expression mixing integer and floating point numbers (like 5 + 0.1), the compiler will automatically convert integer representation into floating point representation so fractions are not lost. Explicit type conversions are either indicated by writing additional code (e.g. adding type identifiers or calling built-in routines) or by coding conversion routines for the compiler to use when it otherwise would halt with a type mismatch.

In most ALGOL-like languages, such as Pascal, Modula-2, Ada and Delphi, conversion and casting are distinctly different concepts. In these languages, conversion refers to either implicitly or explicitly changing a value from one data type storage format to another, e.g. a 16-bit integer to a 32-bit integer. The storage needs may change as a result of the conversion, including a possible loss of precision or truncation. The word cast, on the other hand, refers to explicitly changing the interpretation of the bit pattern representing a value from one type to another. For example, 32 contiguous bits may be treated as an array of 32 Booleans, a 4-byte string, an unsigned 32-bit integer or an IEEE single precision floating point value. Because the stored bits are never changed, the programmer must know low level details such as representation format, byte order, and alignment needs, to meaningfully cast.

In the C family of languages and ALGOL 68, the word cast typically refers to an explicit type conversion (as opposed to an implicit conversion), causing some ambiguity about whether this is a re-interpretation of a bit-pattern or a real data representation conversion. More important is the multitude of ways and rules that apply to what data type (or class) is located by a pointer and how a pointer may be adjusted by the compiler in cases like object (class) inheritance.

Explicit casting in various languages

Ada

Ada provides a generic library function Unchecked_Conversion.

C-like languages

Implicit type conversion

Implicit type conversion, also known as coercion or type juggling, is an automatic type conversion by the compiler. Some programming languages allow compilers to provide coercion; others require it.

In a mixed-type expression, data of one or more subtypes can be converted to a supertype as needed at runtime so that the program will run correctly. For example, the following is legal C language code:

<span id="mweg"></span><span class="kt" id="mwew">double</span><span class="w" id="mwfA"> </span><span class="n" id="mwfQ">d</span><span class="p" id="mwfg">;</span> <span class="kt" id="mwfw">long</span><span class="w" id="mwgA"> </span><span class="n" id="mwgQ">l</span><span class="p" id="mwgg">;</span> <span class="kt" id="mwgw">int</span><span class="w" id="mwhA"> </span><span class="n" id="mwhQ">i</span><span class="p" id="mwhg">;</span> <span class="k" id="mwhw">if</span><span class="w" id="mwiA"> </span><span class="p" id="mwiQ">(</span><span class="n" id="mwig">d</span><span class="w" id="mwiw"> </span><span class="o" id="mwjA">></span><span class="w" id="mwjQ"> </span><span class="n" id="mwjg">i</span><span class="p" id="mwjw">)</span><span class="w" id="mwkA"> </span><span class="p" id="mwkQ">{</span> <span class="w" id="mwkg"> </span><span class="n" id="mwkw">d</span><span class="w" id="mwlA"> </span><span class="o" id="mwlQ">=</span><span class="w" id="mwlg"> </span><span class="n" id="mwlw">i</span><span class="p" id="mwmA">;</span> <span class="p" id="mwmQ">}</span> <span class="k" id="mwmg">if</span><span class="w" id="mwmw"> </span><span class="p" id="mwnA">(</span><span class="n" id="mwnQ">i</span><span class="w" id="mwng"> </span><span class="o" id="mwnw">></span><span class="w" id="mwoA"> </span><span class="n" id="mwoQ">l</span><span class="p" id="mwog">)</span><span class="w" id="mwow"> </span><span class="p" id="mwpA">{</span> <span class="w" id="mwpQ"> </span><span class="n" id="mwpg">l</span><span class="w" id="mwpw"> </span><span class="o" id="mwqA">=</span><span class="w" id="mwqQ"> </span><span class="n" id="mwqg">i</span><span class="p" id="mwqw">;</span> <span class="p" id="mwrA">}</span> <span class="k" id="mwrQ">if</span><span class="w" id="mwrg"> </span><span class="p" id="mwrw">(</span><span class="n" id="mwsA">d</span><span class="w" id="mwsQ"> </span><span class="o" id="mwsg">==</span><span class="w" id="mwsw"> </span><span class="n" id="mwtA">l</span><span class="p" id="mwtQ">)</span><span class="w" id="mwtg"> </span><span class="p" id="mwtw">{</span> <span class="w" id="mwuA"> </span><span class="n" id="mwuQ">d</span><span class="w" id="mwug"> </span><span class="o" id="mwuw">*=</span><span class="w" id="mwvA"> </span><span class="mi" id="mwvQ">2</span><span class="p" id="mwvg">;</span> <span class="p" id="mwvw">}</span>

Although d, l, and i belong to different data types, they will be automatically converted to equal data types each time a comparison or assignment is executed. This behavior should be used with caution, as unintended consequences can arise. Data can be lost when converting representations from floating-point to integer, as the fractional components of the floating-point values will be truncated (rounded toward zero). Conversely, precision can be lost when converting representations from integer to floating-point, since a floating-point type may be unable to exactly represent all possible values of some integer type. For example, float might be an IEEE 754 single precision type, which cannot represent the integer 16777217 exactly, while a 32-bit integer type can. This can lead to unintuitive behavior, as demonstrated by the following code:

<span id="mwzQ"></span><span class="cp" id="mwzg">#include</span><span class="w" id="mwzw"> </span><span class="cpf" id="mw0A"><stdio.h></span> <span class="kt" id="mw0Q">int</span><span class="w" id="mw0g"> </span><span class="nf" id="mw0w">main</span><span class="p" id="mw1A">(</span><span class="kt" id="mw1Q">void</span><span class="p" id="mw1g">)</span><span class="w" id="mw1w"> </span><span class="p" id="mw2A">{</span> <span class="w" id="mw2Q"> </span><span class="kt" id="mw2g">int</span><span class="w" id="mw2w"> </span><span class="n" id="mw3A">my_int</span><span class="w" id="mw3Q"> </span><span class="o" id="mw3g">=</span><span class="w" id="mw3w"> </span><span class="mi" id="mw4A">16777217</span><span class="p" id="mw4Q">;</span> <span class="w" id="mw4g"> </span><span class="kt" id="mw4w">float</span><span class="w" id="mw5A"> </span><span class="n" id="mw5Q">my_float</span><span class="w" id="mw5g"> </span><span class="o" id="mw5w">=</span><span class="w" id="mw6A"> </span><span class="mf" id="mw6Q">16777216.0</span><span class="p" id="mw6g">;</span> <span class="w" id="mw6w"> </span><span class="n" id="mw7A">printf</span><span class="p" id="mw7Q">(</span><span class="s" id="mw7g">"The integer is: %d</span><span class="se" id="mw7w">\n</span><span class="s" id="mw8A">"</span><span class="p" id="mw8Q">,</span><span class="w" id="mw8g"> </span><span class="n" id="mw8w">my_int</span><span class="p" id="mw9A">);</span> <span class="w" id="mw9Q"> </span><span class="n" id="mw9g">printf</span><span class="p" id="mw9w">(</span><span class="s" id="mw-A">"The float is: %f</span><span class="se" id="mw-Q">\n</span><span class="s" id="mw-g">"</span><span class="p" id="mw-w">,</span><span class="w" id="mw_A"> </span><span class="n" id="mw_Q">my_float</span><span class="p" id="mw_g">);</span> <span class="w" id="mw_w"> </span><span class="n" id="mwAQA">printf</span><span class="p" id="mwAQE">(</span><span class="s" id="mwAQI">"Their equality: %d</span><span class="se" id="mwAQM">\n</span><span class="s" id="mwAQQ">"</span><span class="p" id="mwAQU">,</span><span class="w" id="mwAQY"> </span><span class="n" id="mwAQc">my_int</span><span class="w" id="mwAQg"> </span><span class="o" id="mwAQk">==</span><span class="w" id="mwAQo"> </span><span class="n" id="mwAQs">my_float</span><span class="p" id="mwAQw">);</span> <span class="p" id="mwAQ0">}</span>

On compilers that implement floats as IEEE single precision, and ints as at least 32 bits, this code will give this peculiar print-out:

The integer is: 16777217 The float is: 16777216.000000 Their equality: 1

Note that 1 represents equality in the last line above. This odd behavior is caused by an implicit conversion of i_value to float when it is compared with f_value. The conversion causes loss of precision, which makes the values equal before the comparison.

Important takeaways:

  1. float to int causes truncation, i.e., removal of the fractional part.
  2. double to float causes rounding of digit.
  3. long to int causes dropping of excess higher order bits.

Type promotion

One special case of implicit type conversion is type promotion, where an object is automatically converted into another data type representing a superset of the original type. Promotions are commonly used with types smaller than the native type of the target platform's arithmetic logic unit (ALU), before arithmetic and logical operations, to make such operations possible, or more efficient if the ALU can work with more than one type. C and C++ perform such promotion for objects of Boolean, character, wide character, enumeration, and short integer types which are promoted to int, and for objects of type float, which are promoted to double. Unlike some other type conversions, promotions never lose precision or modify the value stored in the object.

In Java:

<span id="mwATc"></span><span class="kt" id="mwATg">int</span><span class="w" id="mwATk"> </span><span class="n" id="mwATo">x</span><span class="w" id="mwATs"> </span><span class="o" id="mwATw">=</span><span class="w" id="mwAT0"> </span><span class="mi" id="mwAT4">3</span><span class="p" id="mwAT8">;</span> <span class="kt" id="mwAUA">double</span><span class="w" id="mwAUE"> </span><span class="n" id="mwAUI">y</span><span class="w" id="mwAUM"> </span><span class="o" id="mwAUQ">=</span><span class="w" id="mwAUU"> </span><span class="mf" id="mwAUY">3.5</span><span class="p" id="mwAUc">;</span> <span class="n" id="mwAUg">System</span><span class="p" id="mwAUk">.</span><span class="na" id="mwAUo">out</span><span class="p" id="mwAUs">.</span><span class="na" id="mwAUw">println</span><span class="p" id="mwAU0">(</span><span class="n" id="mwAU4">x</span><span class="w" id="mwAU8"> </span><span class="o" id="mwAVA">+</span><span class="w" id="mwAVE"> </span><span class="n" id="mwAVI">y</span><span class="p" id="mwAVM">);</span><span class="w" id="mwAVQ"> </span><span class="c1" id="mwAVU">// The output will be 6.5</span>

Explicit type conversion

Explicit type conversion, also called type casting, is a type conversion which is explicitly defined within a program (instead of being done automatically according to the rules of the language for implicit type conversion). It is requested by the user in the program.

<span id="mwAVo"></span><span class="kt" id="mwAVs">double</span><span class="w" id="mwAVw"> </span><span class="n" id="mwAV0">a</span><span class="w" id="mwAV4"> </span><span class="o" id="mwAV8">=</span><span class="w" id="mwAWA"> </span><span class="mf" id="mwAWE">3.3</span><span class="p" id="mwAWI">;</span> <span class="kt" id="mwAWM">double</span><span class="w" id="mwAWQ"> </span><span class="n" id="mwAWU">b</span><span class="w" id="mwAWY"> </span><span class="o" id="mwAWc">=</span><span class="w" id="mwAWg"> </span><span class="mf" id="mwAWk">3.3</span><span class="p" id="mwAWo">;</span> <span class="kt" id="mwAWs">double</span><span class="w" id="mwAWw"> </span><span class="n" id="mwAW0">c</span><span class="w" id="mwAW4"> </span><span class="o" id="mwAW8">=</span><span class="w" id="mwAXA"> </span><span class="mf" id="mwAXE">3.4</span><span class="p" id="mwAXI">;</span> <span class="kt" id="mwAXM">int</span><span class="w" id="mwAXQ"> </span><span class="n" id="mwAXU">result</span><span class="w" id="mwAXY"> </span><span class="o" id="mwAXc">=</span><span class="w" id="mwAXg"> </span><span class="k" id="mwAXk">static_cast</span><span class="o" id="mwAXo"><</span><span class="kt" id="mwAXs">int</span><span class="o" id="mwAXw">></span><span class="p" id="mwAX0">(</span><span class="n" id="mwAX4">a</span><span class="p" id="mwAX8">)</span><span class="w" id="mwAYA"> </span><span class="o" id="mwAYE">+</span><span class="w" id="mwAYI"> </span><span class="k" id="mwAYM">static_cast</span><span class="o" id="mwAYQ"><</span><span class="kt" id="mwAYU">int</span><span class="o" id="mwAYY">></span><span class="p" id="mwAYc">(</span><span class="n" id="mwAYg">b</span><span class="p" id="mwAYk">)</span><span class="w" id="mwAYo"> </span><span class="o" id="mwAYs">+</span><span class="w" id="mwAYw"> </span><span class="k" id="mwAY0">static_cast</span><span class="o" id="mwAY4"><</span><span class="kt" id="mwAY8">int</span><span class="o" id="mwAZA">></span><span class="p" id="mwAZE">(</span><span class="n" id="mwAZI">c</span><span class="p" id="mwAZM">);</span><span class="w" id="mwAZQ"> </span> <span class="c1" id="mwAZU">// result == 9</span> <span class="c1" id="mwAZY">// if implicit conversion would be used (as with "result = a + b + c"), result would be equal to 10</span>

There are several kinds of explicit conversion.

checked
Before the conversion is performed, a runtime check is done to see if the destination type can hold the source value. If not, an error condition is raised.
unchecked
No check is performed. If the destination type cannot hold the source value, the result is undefined.
bit pattern
The raw bit representation of the source is copied verbatim, and it is re-interpreted according to the destination type. This can also be achieved via aliasing.

In object-oriented programming languages, objects can also be downcast : a reference of a base class is cast to one of its derived classes.

C# and C++

In C#, type conversion can be made in a safe or unsafe (i.e., C-like) manner, the former called checked type cast.

<span id="mwAa4"></span><span class="n" id="mwAa8">Animal</span><span class="w" id="mwAbA"> </span><span class="n" id="mwAbE">animal</span><span class="w" id="mwAbI"> </span><span class="o" id="mwAbM">=</span><span class="w" id="mwAbQ"> </span><span class="k" id="mwAbU">new</span><span class="w" id="mwAbY"> </span><span class="n" id="mwAbc">Cat</span><span class="p" id="mwAbg">();</span> <span class="c1" id="mwAbk">// if (animal is Bulldog), stat.type(animal) is Bulldog, else an exception</span> <span class="n" id="mwAbo">Bulldog</span><span class="w" id="mwAbs"> </span><span class="n" id="mwAbw">b</span><span class="w" id="mwAb0"> </span><span class="o" id="mwAb4">=</span><span class="w" id="mwAb8"> </span><span class="p" id="mwAcA">(</span><span class="n" id="mwAcE">Bulldog</span><span class="p" id="mwAcI">)</span><span class="n" id="mwAcM">animal</span><span class="p" id="mwAcQ">;</span> <span class="c1" id="mwAcU">// if (animal is Bulldog), b = (Bulldog)animal, else b = null</span> <span class="n" id="mwAcY">b</span><span class="w" id="mwAcc"> </span><span class="o" id="mwAcg">=</span><span class="w" id="mwAck"> </span><span class="n" id="mwAco">animal</span><span class="w" id="mwAcs"> </span><span class="k" id="mwAcw">as</span><span class="w" id="mwAc0"> </span><span class="n" id="mwAc4">Bulldog</span><span class="p" id="mwAc8">;</span> <span class="c1" id="mwAdA">// remove the reference to Cat(), marking it for garbage collection</span> <span class="n" id="mwAdE">animal</span><span class="w" id="mwAdI"> </span><span class="o" id="mwAdM">=</span><span class="w" id="mwAdQ"> </span><span class="k" id="mwAdU">null</span><span class="p" id="mwAdY">;</span> <span class="c1" id="mwAdc">// b == null</span> <span class="n" id="mwAdg">b</span><span class="w" id="mwAdk"> </span><span class="o" id="mwAdo">=</span><span class="w" id="mwAds"> </span><span class="n" id="mwAdw">animal</span><span class="w" id="mwAd0"> </span><span class="k" id="mwAd4">as</span><span class="w" id="mwAd8"> </span><span class="n" id="mwAeA">Bulldog</span><span class="p" id="mwAeE">;</span>

In C++ a similar effect can be achieved using C++-style cast syntax.

<span id="mwAec"></span><span class="n" id="mwAeg">Animal</span><span class="o" id="mwAek">*</span><span class="w" id="mwAeo"> </span><span class="n" id="mwAes">animal</span><span class="w" id="mwAew"> </span><span class="o" id="mwAe0">=</span><span class="w" id="mwAe4"> </span><span class="k" id="mwAe8">new</span><span class="w" id="mwAfA"> </span><span class="n" id="mwAfE">Cat</span><span class="p" id="mwAfI">();</span> <span class="c1" id="mwAfM">// compiles only if either Animal or Bulldog is derived from the other (or same)</span> <span class="n" id="mwAfQ">Bulldog</span><span class="o" id="mwAfU">*</span><span class="w" id="mwAfY"> </span><span class="n" id="mwAfc">b</span><span class="w" id="mwAfg"> </span><span class="o" id="mwAfk">=</span><span class="w" id="mwAfo"> </span><span class="k" id="mwAfs">static_cast</span><span class="o" id="mwAfw"><</span><span class="n" id="mwAf0">Bulldog</span><span class="o" id="mwAf4">*></span><span class="p" id="mwAf8">(</span><span class="n" id="mwAgA">animal</span><span class="p" id="mwAgE">);</span> <span class="c1" id="mwAgI">// if (animal is Bulldog), b = (Bulldog*) animal, else b = nullptr</span> <span class="n" id="mwAgM">b</span><span class="w" id="mwAgQ"> </span><span class="o" id="mwAgU">=</span><span class="w" id="mwAgY"> </span><span class="k" id="mwAgc">dynamic_cast</span><span class="o" id="mwAgg"><</span><span class="n" id="mwAgk">Bulldog</span><span class="o" id="mwAgo">*></span><span class="p" id="mwAgs">(</span><span class="n" id="mwAgw">animal</span><span class="p" id="mwAg0">);</span> <span class="c1" id="mwAg4">// same as above, but an exception will be thrown if a nullptr was to be returned</span> <span class="c1" id="mwAg8">// this is not seen in code where exception handling is avoided</span> <span class="n" id="mwAhA">Bulldog</span><span class="o" id="mwAhE">&</span><span class="w" id="mwAhI"> </span><span class="n" id="mwAhM">br</span><span class="w" id="mwAhQ"> </span><span class="o" id="mwAhU">=</span><span class="w" id="mwAhY"> </span><span class="k" id="mwAhc">static_cast</span><span class="o" id="mwAhg"><</span><span class="n" id="mwAhk">Bulldog</span><span class="o" id="mwAho">&></span><span class="p" id="mwAhs">(</span><span class="o" id="mwAhw">*</span><span class="n" id="mwAh0">animal</span><span class="p" id="mwAh4">);</span> <span class="c1" id="mwAh8">// deallocate animal after use</span> <span class="k" id="mwAiA">delete</span><span class="w" id="mwAiE"> </span><span class="n" id="mwAiI">animal</span><span class="p" id="mwAiM">;</span> <span class="n" id="mwAiQ">animal</span><span class="w" id="mwAiU"> </span><span class="o" id="mwAiY">=</span><span class="w" id="mwAic"> </span><span class="nb" id="mwAig">nullptr</span><span class="p" id="mwAik">;</span> <span class="c1" id="mwAio">// b == nullptr</span> <span class="n" id="mwAis">b</span><span class="w" id="mwAiw"> </span><span class="o" id="mwAi0">=</span><span class="w" id="mwAi4"> </span><span class="k" id="mwAi8">dynamic_cast</span><span class="o" id="mwAjA"><</span><span class="n" id="mwAjE">Bulldog</span><span class="o" id="mwAjI">*></span><span class="p" id="mwAjM">(</span><span class="n" id="mwAjQ">animal</span><span class="p" id="mwAjU">);</span>

Eiffel

In Eiffel the notion of type conversion is integrated into the rules of the type system. The Assignment Rule says that an assignment, such as x := y, is valid if and only if the type of its source expression (y) is compatible with the type of its target entity (x). In this rule, compatible with means that the type of the source expression either conforms to or converts to that of the target. Conformance of types is defined by the rules for polymorphism in object-oriented programming. For example, in the assignment above, the type of y conforms to the type of x if the class upon which y is based is a descendant of that upon which x is based.

Rust

Rust provides no implicit type conversion (coercion) between most primitive types. But, explicit type conversion (casting) can be performed using the as keyword.

<span id="mwAk8"></span><span class="kd" id="mwAlA">let</span><span class="w" id="mwAlE"> </span><span class="n" id="mwAlI">x</span><span class="p" id="mwAlM">:</span><span class="w" id="mwAlQ"> </span><span class="kt" id="mwAlU">i32</span><span class="w" id="mwAlY"> </span><span class="o" id="mwAlc">=</span><span class="w" id="mwAlg"> </span><span class="mi" id="mwAlk">1000</span><span class="p" id="mwAlo">;</span> <span class="fm" id="mwAls">println!</span><span class="p" id="mwAlw">(</span><span class="s" id="mwAl0">"1000 as a u16 is: {}"</span><span class="p" id="mwAl4">,</span><span class="w" id="mwAl8"> </span><span class="n" id="mwAmA">x</span><span class="w" id="mwAmE"> </span><span class="k" id="mwAmI">as</span><span class="w" id="mwAmM"> </span><span class="kt" id="mwAmQ">u16</span><span class="p" id="mwAmU">);</span>

Sign in to keep reading

3 more chapters are free with a Lably account.

More in Computing and communication