So when you type a number in C#, the name of that kind of token is a "literal". First C# decides if it's an integer literal or a floating-point literal. That comes down to if it has a decimal point or not. The literal 8 is an integer literal. Note in this case "integer" just means "no decimal point", C# hasn't decided on a type yet. This is where things get frustrating when you use types that aren't "normal" like short. C# generally assumes an integer literal is an Int32, the CLR name for int. If an expression it is parsing has an unambiguous reason for it to be interpreted as another type, AND it can safely treat the literal as that type, it will adopt that type for it. So this line works: short bitsPerSample = 16; C# starts by assuming 16 is an int. But then it sees for the = operator, it has to be a short. So it considers the 16 to be a short. All is well. But then we get to a line like this: short ex1 = BitsPerSample / 8; C# operators have a quirk that's confusing if you don't memorize a spec. I hate this. See the documentation: : In the case of integral types, those operators (except the ++ and -- operators) are defined for the int, uint, long, and ulong types. When operands are of other integral types (sbyte, byte, short, ushort, or char), their values are converted to the int type, which is also the result type of an operation So C# does not natively support the idea of short = short / short. It has no concept of an operator that works like: short Divide(short left, short right) The closest C# can come is to give you int = int / int, because it's defined: int Divide(int left, int right) It can convert BitsPerSample to an int just fine. It can convert 8 to an int just fine. But when it divides them, the result is int and to C# it is NOT safe to automatically convert int "down" to short. C# worries about that an int can have a value like 72,000 that isn't valid for short, so it demands that you make a cast. It's a goofy quirk. It's probably done for performance reasons: most CPUs are optimized for 32-bit and 64-bit integer arithmetic. But it also means once you start using "weird" types outside of int, uint, long, and ulong, you have to do more work and write extra casts. Answer from Slypenslyde on reddit.com
🌐
Reddit
reddit.com › r/csharp › why am i being forced to cast to shorts when i am not using any integers? "cannot implicitly convert from int to short" (there are no ints beings used tho?)
r/csharp on Reddit: Why am i being forced to cast to shorts when i am not using any integers? "Cannot implicitly convert from int to short" (There are no ints beings used tho?)
November 11, 2022 - But when it divides them, the result is int and to C# it is NOT safe to automatically convert int "down" to short. C# worries about that an int can have a value like 72,000 that isn't valid for short, so it demands that you make a cast.
Top answer
1 of 2
5

You can't because there's no automatic cast that can be used.

Remember that you can't pass an array to a function by value: an array parameter is really just a pointer parameter, so the following two are the same:

void f(long a[]);
void f(long* a);

When you pass an array to a function, the array is implicitly converted to a pointer to its initial element. So, given long data[10];, the following two are the same:

f(data);
f(&data[0]);

There is no implicit conversion from short* to long*, so if data were declared as short data[10];, you would get a compilation error.

You would need to explicitly cast the short* to a long*, using reinterpret_cast, but that won't convert "an array of N short elements" into "an array of N long elements," it will reinterpret the pointed-to data as "an array of [some number of] long elements," which probably isn't what you want.

You need to create a new array of long elements and copy the elements from the array of short into the array of long:

short data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

long data_as_long[10];
std::copy(data, data + 10, data_as_long);
f(data_as_long);

Or, you might consider changing your function to be a template that can accept a pointer to an array of any type of element:

template <typename T> void f(T*);

Or, for a more advanced solution, the most generic way to do this would be to have your function take an iterator range. This way, you could pass it any type of iterator (including pointers into an array). In addition, it provides a natural and simple way to ensure that the length of the array is passed correctly, since the length is not passed explicitly, it's determined by the arguments.

template <typename Iterator> void f(Iterator first, Iterator last);

const unsigned length_of_data = 10;
long data_array[length_of_data];
std::vector<long> data_vector(length_of_data);

f(data_array, data_array + length_of_data); // works!
f(data_vector.begin(), data_vector.end());  // also works!
2 of 2
1

No, a good compiler will give an error. A bad compiler will give strange results.

Recasting a single element at a time is common and presents no difficulties, but passing a pointer to a function expecting one type of object and receiving another is an I Love Lucy sort of catastrophe.

Discussions

What is the rule for C to cast between short and int? - Stack Overflow
I'm confused when using C to cast between short and int. I assume short is 16-bit and int is 32-bit. I tested with below code: unsigned short a = 0xFFFF; signed short b = 0xFFFF; unsigned int u16... More on stackoverflow.com
🌐 stackoverflow.com
casting - What happens when I assign long int to int in C? - Stack Overflow
In a recent homework assignment I've been told to use long variable to store a result, since it may be a big number. I decided to check will it really matter for me, on my system (intel core i5/64... More on stackoverflow.com
🌐 stackoverflow.com
Is C guaranteed to correctly convert "long" and friends as "int"?
No. It is UB. Strict aliasing rule are violated. See 6.5p7 : An object shall have its stored value accessed only by an lvalue expression that has one of the following types: a type compatible with the effective type of the object, a qualified version of a type compatible with the effective type of the object, a type that is the signed or unsigned type corresponding to the effective type of the object, a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object, an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or a character type. Type int and long int are not compatible and they are not enlisted on the list of exceptions above. Thus undefined behavior is invoked. However, the program is defined for unsigned int due to "a type that is the signed or unsigned type corresponding to the effective type of the object" exception. More on reddit.com
🌐 r/C_Programming
25
1
October 11, 2022
c# - Cannot implicitly convert type 'long' to "int?"? - Stack Overflow
It a very simple question - convert a type long variable to a Nullable int type (int?). (see example below- not the actual code) int? y = 100; long x = long.MaxValue; y = x; I am getting compile... More on stackoverflow.com
🌐 stackoverflow.com
Top answer
1 of 4
8

Anytime an integer type is being converted to a different integer type it falls through a deterministic pachinko machine of rules as dictated by the standard and on one occasion, the implementation.

The general overview on value-qualification:

C99 6.3.1.1-p2

If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.

That said, lets look at your conversions. The signed-short to unsigned int is covered by the following, since the value being converted falls outside the unsigned int domain:

C99 6.3.1.3-p2

Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.

Which basically means "add UINT_MAX+1". On your machine, UINT_MAX is 4294967295, therefore, this becomes

-1 + 4294967295 + 1 = 4294967295

Regarding your unsigned short to signed int conversion, that is covered by the regular value-quaified promotion. Specifically:

C99 6.3.1.3-p1

When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.

In other words, because the value of your unsigned short falls within the coverable domain of signed int, there is nothing special done and the value is simply saved.

And finally, as mentioned in general-comment above, something special happens to your declaration of b

signed short b = 0xFFFF;

The 0xFFFF in this case is a signed integer. The decimal value is 65535. However, that value is not representable by a signed short so yet-another conversion happens, one that perhaps you weren't aware of:

C99 6.3.1.3-p3

Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

In other words, your implementation chose to store it as (-1), but you cannot rely on that on a different implementation.

2 of 4
5

As stated in the question, assume 16-bit short and 32-bit int.

unsigned short a = 0xFFFF;

This initializes a to 0xFFFF, or 65535. The expression 0xFFFF is of type int; it's implicitly converted to unsigned short, and the value is preserved.

signed short b = 0xFFFF;

This is a little more complicated. Again, 0xFFFF is of type int. It's implicitly converted to signed short -- but since the value is outside the range of signed short the conversion cannot preserve the value.

Conversion of an integer to a signed integer type, when the value can't be represented, yields an implementation-defined value. In principle, the value of b could be anything between -32768 and +32767 inclusive. In practice, it will almost certainly be -1. I'll assume for the rest of this that the value is -1.

unsigned int u16tou32 = a;

The value of a is 0xFFFF, which is converted from unsigned short to unsigned int. The conversion preserves the value.

unsigned int s16tou32 = b;

The value of b is -1. It's converted to unsigned int, which clearly cannot store a value of -1. Conversion of an integer to an unsigned integer type (unlike conversion to a signed type) is defined by the language; the result is reduced modulo MAX + 1, where MAX is the maximum value of the unsigned type. In this case, the value stored in s16tou32 is UINT_MAX - 1, or 0xFFFFFFFF.

signed int u16tos32 = a;

The value of a, 0xFFFF, is converted to signed int. The value is preserved.

signed int s16tos32 = b;

The value of b, -1, is converted to signed int. The value is preserved.

So the stored values are:

a == 0xFFFF (65535)
b == -1     (not guaranteed, but very likely)
u16tou32 == 0xFFFF (65535)
s16tou32 == 0xFFFFFFFF (4294967295)
u16tos32 == 0xFFFF (65535)
s16tos32 == -1

To summarize the integer conversion rules:

If the target type can represent the value, the value is preserved.

Otherwise, if the target type is unsigned, the value is reduced modulo MAX+1, which is equivalent to discarding all but the low-order N bits. Another way to describe this is that the value MAX+1 is repeatedly added to or subtracted from the value until you get a result that's in the range (this is actually how the C standard describes it). Compilers don't actually generate code to do this repeated addition or subtraction; they just have to get the right result.

Otherwise, the target type is signed and cannot represent the value; the conversion yields an implementation-defined value. In almost all implementations, the result discards all but the low-order N bits using a two's-complement representation. (C99 added a rule for this case, permitting an implementation-defined signal to be raised instead. I don't know of any compiler that does this.)

🌐
SEI CERT
wiki.sei.cmu.edu › confluence › display › c › INT02-C.+Understand+integer+conversion+rules
INT02-C. Understand integer conversion rules - SEI CERT C Coding Standard - Confluence
Although conversions are generally required for the correct execution of a program, they can also lead to lost or misinterpreted data. Conversion of an operand value to a compatible type causes no change to the value or the representation. The C integer conversion rules define how C compilers ...
🌐
Microsoft Learn
learn.microsoft.com › en-us › cpp › cpp › type-conversions-and-type-safety-modern-cpp
Type conversions and type safety | Microsoft Learn
June 18, 2025 - This means that every variable, ... bit patterns, or memory corruption. A program that never explicitly or implicitly converts values from one type to another is type-safe by definition....
🌐
TheLinuxCode
thelinuxcode.com › home › implicit type casting: a practical guide to silent conversions (and how to stay safe)
Implicit Type Casting: A Practical Guide to Silent Conversions (and How to Stay Safe) – TheLinuxCode
February 11, 2026 - The important nuance: “automatic” does not mean “safe.” Languages define what is allowed implicitly, and those rules vary dramatically. A good first mental model is to split implicit casting into two buckets: 1) Conversions intended to preserve meaning (often called widening) Example: ...
Top answer
1 of 2
52

The language guarantees that int is at least 16 bits, long is at least 32 bits, and long can represent at least all the values that int can represent.

If you assign a long value to an int object, it will be implicitly converted. There's no need for an explicit cast; it would merely specify the same conversion that's going to happen anyway.

On your system, where int and long happen to have the same size and range, the conversion is trivial; it simply copies the value.

On a system where long is wider than int, if the value won't fit in an int, then the result of the conversion is implementation-defined. (Or, starting in C99, it can raise an implementation-defined signal, but I don't know of any compilers that actually do that.) What typically happens is that the high-order bits are discarded, but you shouldn't depend on that. (The rules are different for unsigned types; the result of converting a signed or unsigned integer to an unsigned type is well defined.)

If you need to safely assign a long value to an int object, you can check that it will fit before doing the assignment:

#include <limits.h> /* for INT_MIN, INT_MAX */

/* ... */

int i;
long li = /* whatever */

if (li >= INT_MIN && li <= INT_MAX) {
    i = li;
}
else {
    /* do something else? */
}

The details of "something else" are going to depend on what you want to do.

One correction: int and long are always distinct types, even if they happen to have the same size and representation. Arithmetic types are freely convertible, so this often doesn't make any difference, but for example int* and long* are distinct and incompatible types; you can't assign a long* to an int*, or vice versa, without an explicit (and potentially dangerous) cast.

And if you find yourself needing to convert a long value to int, the first thing you should do is reconsider your code's design. Sometimes such conversions are necessary, but more often they're a sign that the int to which you're assigning should have been defined as a long in the first place.

2 of 2
4

A long can always represent all values of int. If the value at hand can be represented by the type of the variable you assign to, then the value is preserved.

If it can't be represented, then for signed destination type the result is formally unspecified, while for unsigned destination type it is specified as the original value modulo 2n, where n is the number of bits in the value representation (which is not necessarily all the bits in the destination).

In practice, on modern machines you get wrapping also for signed types.

That's because modern machines use two's complement form to represent signed integers, without any bits used to denote "invalid value" or such – i.e., all bits used for value representation.

With n bits value representation any integer value is x is mapped to x+K*2n with the integer constant K chosen such that the result is in the range where half of the possible values are negative.

Thus, for example, with 32-bit int the value -7 is represented as bitpattern number -7+232 = 232-7, so that if you display the number that the bitpattern stands for as unsigned integer, you get a pretty large number.

The reason that this is called two's complement is because it makes sense for the binary numeral system, the base two numeral system. For the binary numeral system there's also a ones' (note the placement of the apostrophe) complement. Similarly, for the decimal numberal system there's ten's complement and niners' complement. With 4 digit ten's complement representation you would represent -7 as 10000-7 = 9993. That's all, really.

Find elsewhere
🌐
Cppreference
en.cppreference.com › w › cpp › language › implicit_cast.html
Implicit conversions - cppreference.com
The following implicit conversions in this section are classified as integral promotions. Note that for a given source type, the destination type of integral promotion is unique, And all other conversions are not promotions. For example, overload resolution chooses char -> int (promotion) over char -> short ...
🌐
Cplusplus
cplusplus.com › doc › tutorial › typecasting
Type-cast
This may or may not be what was intended. But, in any case, it can be prevented by marking the affected constructor with the explicit keyword: Additionally, constructors marked with explicit cannot be called with the assignment-like syntax; In the above example, bar could not have been constructed with: Type-cast member functions (those described in the previous section) can also be specified as explicit. This prevents implicit conversions in the same way as explicit-specified constructors do for the destination type.
🌐
AlgoMaster
algomaster.io › learn › java › type-casting
Type Casting | Java | AlgoMaster.io | AlgoMaster.io
Java automatically handles this conversion because it is safe and does not lose information. Explicit Casting (Narrowing Conversion): This is necessary when you want to convert a larger data type to a smaller one.
🌐
Reddit
reddit.com › r/c_programming › is c guaranteed to correctly convert "long" and friends as "int"?
r/C_Programming on Reddit: Is C guaranteed to correctly convert "long" and friends as "int"?
October 11, 2022 -

I'm using a library function which takes arrays of long's as inputs (and single long's too), and modifies these arrays. As long as all the numbers inside the library stay within the int size limit (i.e. there are no calculations that would overflow int), is it 100% safe for me to pass it arrays of int's, or should I manually typecast my arrays using for loops from int to long before input? And then assume that these arrays still contain int's?

What if this was some other integer type, like unsigned int?

I am worried that, for example, number 5 has different bit representation (and is stored using a different number of bits) across these various data types. Will C figure it out and do the correct thing?

Top answer
1 of 3
9
No. It is UB. Strict aliasing rule are violated. See 6.5p7 : An object shall have its stored value accessed only by an lvalue expression that has one of the following types: a type compatible with the effective type of the object, a qualified version of a type compatible with the effective type of the object, a type that is the signed or unsigned type corresponding to the effective type of the object, a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object, an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or a character type. Type int and long int are not compatible and they are not enlisted on the list of exceptions above. Thus undefined behavior is invoked. However, the program is defined for unsigned int due to "a type that is the signed or unsigned type corresponding to the effective type of the object" exception.
2 of 3
5
long and int can have different sizing depending on platform. On Windows, they're both 32 bit so you probably won't have an issue (though still technically undefined behavior). On x86_64 Linux though, int is 32 bit while long is 64 bit. What will happen on Linux if you pass in your array of int is it'll read in 2 of your 32 bit elements thinking it's 1 64 bit element giving you a completely different number. Then it'll probably read outside the bounds of your array producing undefined behavior. You'll need to create a new array of longs and use a loop to convert them before you pass it off to the library. Now if the function is taking in a single long by value, it's fine to give it an int. C will do the conversion for you. It's only a problem with passing arrays and pointers.
🌐
Medium
medium.com › @AlexanderObregon › beginners-guide-to-java-type-casting-0b31d543ddcc
Beginner’s Guide to Java Type Casting
March 11, 2024 - The rationale behind implicit casting is straightforward: Java aims to preserve data integrity and precision. For example, converting an integer (which is 32 bits in Java) into a double (which is 64 bits) can be done without any loss of precision, ...
🌐
C++ Stories
cppstories.com › 2022 › safe-int-cmp-cpp20
Integer Conversions and Safe Comparisons in C++20 - C++ Stories
September 12, 2022 - Putting casts here and there might not be the best idea, not to mention the code style. Additionally, we should also follow the C++ Core Guideline Rule: ES.100: Don’t mix signed and unsigned arithmetic: Reason Avoid wrong results. Fortunately, in C++20, we have a utility to handle such situations. It’s called “Safe Integral Comparisons” - P0586 by Federico Kircheis.
🌐
TutorialsPoint
tutorialspoint.com › cprogramming › c_type_casting.htm
Type Casting in C
There are certain times when the compiler does the conversion on its own (implicit type conversion), so that the data types are compatible with each other. On other occasions, the C compiler forcefully performs the typecasting (explicit type conversion), which is caused by the typecasting operator. For example, if you want to store a 'long' value into a simple integer then you can type cast 'long' to 'int'...
🌐
Oracle
docs.oracle.com › javase › specs › jls › se7 › html › jls-5.html
Chapter 5. Conversions and Promotions
2 weeks ago - causes a compile-time error because the integer literals 12 and 2 have type int, so neither method m matches under the rules of (§15.12.2). A language that included implicit narrowing of integer constants would need additional rules to resolve cases like this example. String conversion applies only to an operand of the binary + operator which is not a String when the other operand is a String. In this single special case, the non-String operand to the + is converted to a String (§5.1.11) and evaluation of the + operator proceeds as specified in §15.18.1. Casting conversion is applied to the operand of a cast operator (§15.16): the type of the operand expression must be converted to the type explicitly named by the cast operator.
🌐
CodeGym
codegym.cc › java blog › java types › java type casting
Java Type Casting
April 23, 2025 - There is no implicit conversion from numeric to char or boolean type. In Java, char and boolean types are non-compatible. byte -> short -> char -> int -> long -> float -> double This type of casting is done automatically by the compiler without ...
🌐
tzimmermann dot org
tzimmermann.org › 2018 › 04 › 20 › safe-integer-conversion-in-c
Safe Integer Conversion in C • tzimmermann dot org
April 20, 2018 - The first test is now a signed-to-signed conversion, which is always safe to perform. The second test is also safe to perform, as the range of short can represent UCHAR_MAX. Ideally, we’d have a single function for each conversion, something ...