How does casting really work for primitive data types? [closed]
Can a non-EU citizen travel within the Schengen area without identity documents?
Why is there a need to modify system call tables in linux?
Mother abusing my finances
Is the world in Game of Thrones spherical or flat?
Draw a checker pattern with a black X in the center
Team member doesn't give me the minimum time to complete a talk
Is there an explanation for Austria's Freedom Party virtually retaining its vote share despite recent scandal?
Can a wire having a 610-670 THz (frequency of blue light) AC frequency supply, generate blue light?
Did airlines fly their aircraft slower in response to oil prices in the 1970s?
Points within polygons in different projections
Is floating in space similar to falling under gravity?
How should I push back against my job assigning "homework"?
Why do Russians call their women expensive ("дорогая")?
My player wants to cast multiple charges of magic missile from a wand
Looking after a wayward brother in mother's will
Where can I find the list of all tendons in the human body?
How crucial is a waifu game storyline?
Could I be denied entry into Ireland due to medical and police situations during a previous UK visit?
If a massive object like Jupiter flew past the Earth how close would it need to come to pull people off of the surface?
How to prevent bad sectors?
What caused the tendency for conservatives to not support climate change regulations?
Different PCB color ( is it different material? )
How can I offer a test ride while selling a bike?
Can an old DSLR be upgraded to match modern smartphone image quality
How does casting really work for primitive data types? [closed]
$begingroup$
I have written the following C code which purpose it is to flip an unsigned number in binary representation and give back an unsigned number:
long flippingBits(long n)
return (unsigned)(~n);
int main()
long a = flippingBits(someNumber);
...
This code does work fine. What about this code?
unsigned long flippingBits(long n)
return (~n);
int main()
unsigned long a = flippingBits(someNumber);
...
This code indeed prints out negative numbers. WTF. This is beyond my intuition. So is there an (architectural) way, to explain this phenomenon? How does the casting of primitive data types work?
c
$endgroup$
closed as off-topic by David Richerby, xskxzr, chi, Evil, Gilles♦ May 15 at 21:50
This question appears to be off-topic. The users who voted to close gave this specific reason:
- "Questions about software development or programming tools are off-topic here, but can be asked on Stack Overflow." – David Richerby, xskxzr, chi, Evil
add a comment |
$begingroup$
I have written the following C code which purpose it is to flip an unsigned number in binary representation and give back an unsigned number:
long flippingBits(long n)
return (unsigned)(~n);
int main()
long a = flippingBits(someNumber);
...
This code does work fine. What about this code?
unsigned long flippingBits(long n)
return (~n);
int main()
unsigned long a = flippingBits(someNumber);
...
This code indeed prints out negative numbers. WTF. This is beyond my intuition. So is there an (architectural) way, to explain this phenomenon? How does the casting of primitive data types work?
c
$endgroup$
closed as off-topic by David Richerby, xskxzr, chi, Evil, Gilles♦ May 15 at 21:50
This question appears to be off-topic. The users who voted to close gave this specific reason:
- "Questions about software development or programming tools are off-topic here, but can be asked on Stack Overflow." – David Richerby, xskxzr, chi, Evil
5
$begingroup$
This isn’t Computer Science, but very basic C programming. But since you are here, I’d advise you to google for “C Standard Draft” and use the document you find.
$endgroup$
– gnasher729
May 15 at 15:09
1
$begingroup$
There is no print command in the second example. What do you mean by "the code indeed prints out negative numbers"?
$endgroup$
– JiK
May 15 at 19:24
add a comment |
$begingroup$
I have written the following C code which purpose it is to flip an unsigned number in binary representation and give back an unsigned number:
long flippingBits(long n)
return (unsigned)(~n);
int main()
long a = flippingBits(someNumber);
...
This code does work fine. What about this code?
unsigned long flippingBits(long n)
return (~n);
int main()
unsigned long a = flippingBits(someNumber);
...
This code indeed prints out negative numbers. WTF. This is beyond my intuition. So is there an (architectural) way, to explain this phenomenon? How does the casting of primitive data types work?
c
$endgroup$
I have written the following C code which purpose it is to flip an unsigned number in binary representation and give back an unsigned number:
long flippingBits(long n)
return (unsigned)(~n);
int main()
long a = flippingBits(someNumber);
...
This code does work fine. What about this code?
unsigned long flippingBits(long n)
return (~n);
int main()
unsigned long a = flippingBits(someNumber);
...
This code indeed prints out negative numbers. WTF. This is beyond my intuition. So is there an (architectural) way, to explain this phenomenon? How does the casting of primitive data types work?
c
c
edited May 15 at 21:50
Gilles♦
33.8k798166
33.8k798166
asked May 15 at 15:00
TVSuchtyTVSuchty
614
614
closed as off-topic by David Richerby, xskxzr, chi, Evil, Gilles♦ May 15 at 21:50
This question appears to be off-topic. The users who voted to close gave this specific reason:
- "Questions about software development or programming tools are off-topic here, but can be asked on Stack Overflow." – David Richerby, xskxzr, chi, Evil
closed as off-topic by David Richerby, xskxzr, chi, Evil, Gilles♦ May 15 at 21:50
This question appears to be off-topic. The users who voted to close gave this specific reason:
- "Questions about software development or programming tools are off-topic here, but can be asked on Stack Overflow." – David Richerby, xskxzr, chi, Evil
5
$begingroup$
This isn’t Computer Science, but very basic C programming. But since you are here, I’d advise you to google for “C Standard Draft” and use the document you find.
$endgroup$
– gnasher729
May 15 at 15:09
1
$begingroup$
There is no print command in the second example. What do you mean by "the code indeed prints out negative numbers"?
$endgroup$
– JiK
May 15 at 19:24
add a comment |
5
$begingroup$
This isn’t Computer Science, but very basic C programming. But since you are here, I’d advise you to google for “C Standard Draft” and use the document you find.
$endgroup$
– gnasher729
May 15 at 15:09
1
$begingroup$
There is no print command in the second example. What do you mean by "the code indeed prints out negative numbers"?
$endgroup$
– JiK
May 15 at 19:24
5
5
$begingroup$
This isn’t Computer Science, but very basic C programming. But since you are here, I’d advise you to google for “C Standard Draft” and use the document you find.
$endgroup$
– gnasher729
May 15 at 15:09
$begingroup$
This isn’t Computer Science, but very basic C programming. But since you are here, I’d advise you to google for “C Standard Draft” and use the document you find.
$endgroup$
– gnasher729
May 15 at 15:09
1
1
$begingroup$
There is no print command in the second example. What do you mean by "the code indeed prints out negative numbers"?
$endgroup$
– JiK
May 15 at 19:24
$begingroup$
There is no print command in the second example. What do you mean by "the code indeed prints out negative numbers"?
$endgroup$
– JiK
May 15 at 19:24
add a comment |
2 Answers
2
active
oldest
votes
$begingroup$
Casting of primitive datatypes works in a very simple way:
Under the hood, values don't have types. Casting does absolutely nothing.
As a simple example, let us consider casting 65 to an ASCII character, thus obtaining the symbol A
. What happens under the hood? Absolutely nothing. The only thing that changes is how the program treats the value. When it thinks of it as an integer, it sees 65; when it thinks of it as an ASCII character it sees A
.
The CPU does accommodate different datatypes when performing various operations. For example, it has separate instructions for signed and unsigned integer arithmetic, and for floating point arithmetic. Moreover, there are different instruction for different lengths (i.e. 16-bit vs. 32-bit vs. 64-bit for integers). But it is up to the programmer to instruct the CPU which instruction to use. The CPU doesn't keep track of datatypes, which are a higher-level concept.
Modern CPUs do keep a distinction between code and data to some extent, for security purposes. But otherwise the semantics of data is up to the programmer.
Sometimes casting does have an actual effect, and this is during promotion. When casting an integer to a floating point number, or a 32-bit integer to a 64-bit integer, the program calls some instruction which effects this conversion (in the second case, the exact instruction depends on whether the integer is signed or unsigned).
When casting a signed integer to an unsigned integer of the same length (or vice versa), probably nothing happens. This is exactly your case.
$endgroup$
3
$begingroup$
"Casting does absolutely nothing" is too broad a statement to lead with. It only does nothing for this particular cast, and only because of the signed and unsigned integer representations we've chosen. Especially for integer->floating or floating->integer casts, the new and old bit patterns in memory could be completely different. If offset binary had somehow become the standard for signed integers, unsigned->signed and signed->unsigned would change the bit pattern too.
$endgroup$
– user2357112
May 15 at 19:02
1
$begingroup$
(We don't have to worry about offset binary in our timeline, and it's forbidden by the C standard, but if things had played out differently, we could have had to deal with integer formats where the signed and unsigned representations of the same nonnegative number don't match.)
$endgroup$
– user2357112
May 15 at 19:02
$begingroup$
The main point is that datatypes are an artificial construct. Down below it's just bits. Only the program can give them meaning.
$endgroup$
– Yuval Filmus
May 15 at 19:03
1
$begingroup$
Plenty of things are misleading or inaccurate in this answer. You're describing a sort of idealized 1970s C on a typical byte-oriented machine, but modern compilers have optimizations that make the semantics of practical-C a lot more subtle, while remaining compliant with the semantics defined by the C standard. “Casting does absolutely nothing” is wrong by any perspective. Between integer types of different ranges, it can apply a modulo operation (or sometimes other effects). When floating point is involved, it can approximate.
$endgroup$
– Gilles♦
May 15 at 21:58
1
$begingroup$
(cont.) Promotion is a technical term for certain automatic conversions. (A conversion is basically an implicit cast, and a cast is basically an explicit conversion.) There is no casting between'A'
and65
: both are literals of typeint
. When casting a signed integer to an unsigned integer, the value and (on virtually all implementations) the bit pattern are preserved only if the value is non-negative.
$endgroup$
– Gilles♦
May 15 at 22:00
|
show 1 more comment
$begingroup$
How does the casting of primitive datatypes works?
The restriction "primitive datatype" is not enough:
Casting from a 16-bit integer to a 32-bit integer works differently than casting between integer types of the same size. And casting between integer and floating-point types is even more complex.
This Code does work fine. What about this code?
Both code examples will work differently on different CPUs, operating systems and possibly using different compilers.
This is because under some operating systems (e.g. 32-bit Windows) long
and unsigned
data types have the same number of bits; under other operating systems (e.g. 64-bit Windows) long
has twice as many bits as unsigned
.
And you use the ~
operator (~n)
on a signed data type which may cause different results depending on the compiler you use.
This code indeed prints out negative numbers.
So is there an (architectural) way, to explain this phenomenon?
There are two effects:
The first effect is:
If you pass a value of a wrong data type to some function, C will automatically cast the value.
And if you are casting a value that is outside the range of the data type you are casting to, the result will typically not be what you are intuitively expecting.
In the case of two integer data types, normally the "rightmost" bits of the value are copied to the new value.
Let's take (signed char)3478
as an example:
The number 3478 is written as 110110010110
in binary. signed char
is an 8 bit data type. The rightmost 8 bits of 1101 10010110
are 10010110
. And the value (-106) is stored as 10010110
in a signed char
variable.
Therefore (signed char)3478
will result in (-106).
The value a
is stored in an unsigned long
variable which means that it cannot be negative!
However, you say that you "print out" the value of a
. This means you pass the value to some function that prints it to the screen.
Obviously that function expects some signed data type. C will cast the value stored in a
to the signed data type and as in the example with the number 3478, the result of casting may be a negative number.
If you use printf
you may see a second effect:
If some function (such as printf
) has a variable number of parameters, the way the parameters are passed to the function is depending on the data types passed. (The details may also vary from OS to OS.)
You may now pass the wrong data type to the function; for example you use "%d"
(signed int
) in the printf
format string but you pass an unsigned long
.
If you do this, the compiler will write the value of a
to some data storage (memory or register) which is suitable for unsigned long
values before actually calling the function. (For such functions the compiler is not able to find out which data type is really expected by the function, so it has to assume that the function expects the data type you are passing to the function.)
However, printf
will look for a value in some data storage suitable for signed int
values which might be located somewhere completely different. That data storage does not contain any useful value because the compiler wrote the value of a
somewhere else!
$endgroup$
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
Casting of primitive datatypes works in a very simple way:
Under the hood, values don't have types. Casting does absolutely nothing.
As a simple example, let us consider casting 65 to an ASCII character, thus obtaining the symbol A
. What happens under the hood? Absolutely nothing. The only thing that changes is how the program treats the value. When it thinks of it as an integer, it sees 65; when it thinks of it as an ASCII character it sees A
.
The CPU does accommodate different datatypes when performing various operations. For example, it has separate instructions for signed and unsigned integer arithmetic, and for floating point arithmetic. Moreover, there are different instruction for different lengths (i.e. 16-bit vs. 32-bit vs. 64-bit for integers). But it is up to the programmer to instruct the CPU which instruction to use. The CPU doesn't keep track of datatypes, which are a higher-level concept.
Modern CPUs do keep a distinction between code and data to some extent, for security purposes. But otherwise the semantics of data is up to the programmer.
Sometimes casting does have an actual effect, and this is during promotion. When casting an integer to a floating point number, or a 32-bit integer to a 64-bit integer, the program calls some instruction which effects this conversion (in the second case, the exact instruction depends on whether the integer is signed or unsigned).
When casting a signed integer to an unsigned integer of the same length (or vice versa), probably nothing happens. This is exactly your case.
$endgroup$
3
$begingroup$
"Casting does absolutely nothing" is too broad a statement to lead with. It only does nothing for this particular cast, and only because of the signed and unsigned integer representations we've chosen. Especially for integer->floating or floating->integer casts, the new and old bit patterns in memory could be completely different. If offset binary had somehow become the standard for signed integers, unsigned->signed and signed->unsigned would change the bit pattern too.
$endgroup$
– user2357112
May 15 at 19:02
1
$begingroup$
(We don't have to worry about offset binary in our timeline, and it's forbidden by the C standard, but if things had played out differently, we could have had to deal with integer formats where the signed and unsigned representations of the same nonnegative number don't match.)
$endgroup$
– user2357112
May 15 at 19:02
$begingroup$
The main point is that datatypes are an artificial construct. Down below it's just bits. Only the program can give them meaning.
$endgroup$
– Yuval Filmus
May 15 at 19:03
1
$begingroup$
Plenty of things are misleading or inaccurate in this answer. You're describing a sort of idealized 1970s C on a typical byte-oriented machine, but modern compilers have optimizations that make the semantics of practical-C a lot more subtle, while remaining compliant with the semantics defined by the C standard. “Casting does absolutely nothing” is wrong by any perspective. Between integer types of different ranges, it can apply a modulo operation (or sometimes other effects). When floating point is involved, it can approximate.
$endgroup$
– Gilles♦
May 15 at 21:58
1
$begingroup$
(cont.) Promotion is a technical term for certain automatic conversions. (A conversion is basically an implicit cast, and a cast is basically an explicit conversion.) There is no casting between'A'
and65
: both are literals of typeint
. When casting a signed integer to an unsigned integer, the value and (on virtually all implementations) the bit pattern are preserved only if the value is non-negative.
$endgroup$
– Gilles♦
May 15 at 22:00
|
show 1 more comment
$begingroup$
Casting of primitive datatypes works in a very simple way:
Under the hood, values don't have types. Casting does absolutely nothing.
As a simple example, let us consider casting 65 to an ASCII character, thus obtaining the symbol A
. What happens under the hood? Absolutely nothing. The only thing that changes is how the program treats the value. When it thinks of it as an integer, it sees 65; when it thinks of it as an ASCII character it sees A
.
The CPU does accommodate different datatypes when performing various operations. For example, it has separate instructions for signed and unsigned integer arithmetic, and for floating point arithmetic. Moreover, there are different instruction for different lengths (i.e. 16-bit vs. 32-bit vs. 64-bit for integers). But it is up to the programmer to instruct the CPU which instruction to use. The CPU doesn't keep track of datatypes, which are a higher-level concept.
Modern CPUs do keep a distinction between code and data to some extent, for security purposes. But otherwise the semantics of data is up to the programmer.
Sometimes casting does have an actual effect, and this is during promotion. When casting an integer to a floating point number, or a 32-bit integer to a 64-bit integer, the program calls some instruction which effects this conversion (in the second case, the exact instruction depends on whether the integer is signed or unsigned).
When casting a signed integer to an unsigned integer of the same length (or vice versa), probably nothing happens. This is exactly your case.
$endgroup$
3
$begingroup$
"Casting does absolutely nothing" is too broad a statement to lead with. It only does nothing for this particular cast, and only because of the signed and unsigned integer representations we've chosen. Especially for integer->floating or floating->integer casts, the new and old bit patterns in memory could be completely different. If offset binary had somehow become the standard for signed integers, unsigned->signed and signed->unsigned would change the bit pattern too.
$endgroup$
– user2357112
May 15 at 19:02
1
$begingroup$
(We don't have to worry about offset binary in our timeline, and it's forbidden by the C standard, but if things had played out differently, we could have had to deal with integer formats where the signed and unsigned representations of the same nonnegative number don't match.)
$endgroup$
– user2357112
May 15 at 19:02
$begingroup$
The main point is that datatypes are an artificial construct. Down below it's just bits. Only the program can give them meaning.
$endgroup$
– Yuval Filmus
May 15 at 19:03
1
$begingroup$
Plenty of things are misleading or inaccurate in this answer. You're describing a sort of idealized 1970s C on a typical byte-oriented machine, but modern compilers have optimizations that make the semantics of practical-C a lot more subtle, while remaining compliant with the semantics defined by the C standard. “Casting does absolutely nothing” is wrong by any perspective. Between integer types of different ranges, it can apply a modulo operation (or sometimes other effects). When floating point is involved, it can approximate.
$endgroup$
– Gilles♦
May 15 at 21:58
1
$begingroup$
(cont.) Promotion is a technical term for certain automatic conversions. (A conversion is basically an implicit cast, and a cast is basically an explicit conversion.) There is no casting between'A'
and65
: both are literals of typeint
. When casting a signed integer to an unsigned integer, the value and (on virtually all implementations) the bit pattern are preserved only if the value is non-negative.
$endgroup$
– Gilles♦
May 15 at 22:00
|
show 1 more comment
$begingroup$
Casting of primitive datatypes works in a very simple way:
Under the hood, values don't have types. Casting does absolutely nothing.
As a simple example, let us consider casting 65 to an ASCII character, thus obtaining the symbol A
. What happens under the hood? Absolutely nothing. The only thing that changes is how the program treats the value. When it thinks of it as an integer, it sees 65; when it thinks of it as an ASCII character it sees A
.
The CPU does accommodate different datatypes when performing various operations. For example, it has separate instructions for signed and unsigned integer arithmetic, and for floating point arithmetic. Moreover, there are different instruction for different lengths (i.e. 16-bit vs. 32-bit vs. 64-bit for integers). But it is up to the programmer to instruct the CPU which instruction to use. The CPU doesn't keep track of datatypes, which are a higher-level concept.
Modern CPUs do keep a distinction between code and data to some extent, for security purposes. But otherwise the semantics of data is up to the programmer.
Sometimes casting does have an actual effect, and this is during promotion. When casting an integer to a floating point number, or a 32-bit integer to a 64-bit integer, the program calls some instruction which effects this conversion (in the second case, the exact instruction depends on whether the integer is signed or unsigned).
When casting a signed integer to an unsigned integer of the same length (or vice versa), probably nothing happens. This is exactly your case.
$endgroup$
Casting of primitive datatypes works in a very simple way:
Under the hood, values don't have types. Casting does absolutely nothing.
As a simple example, let us consider casting 65 to an ASCII character, thus obtaining the symbol A
. What happens under the hood? Absolutely nothing. The only thing that changes is how the program treats the value. When it thinks of it as an integer, it sees 65; when it thinks of it as an ASCII character it sees A
.
The CPU does accommodate different datatypes when performing various operations. For example, it has separate instructions for signed and unsigned integer arithmetic, and for floating point arithmetic. Moreover, there are different instruction for different lengths (i.e. 16-bit vs. 32-bit vs. 64-bit for integers). But it is up to the programmer to instruct the CPU which instruction to use. The CPU doesn't keep track of datatypes, which are a higher-level concept.
Modern CPUs do keep a distinction between code and data to some extent, for security purposes. But otherwise the semantics of data is up to the programmer.
Sometimes casting does have an actual effect, and this is during promotion. When casting an integer to a floating point number, or a 32-bit integer to a 64-bit integer, the program calls some instruction which effects this conversion (in the second case, the exact instruction depends on whether the integer is signed or unsigned).
When casting a signed integer to an unsigned integer of the same length (or vice versa), probably nothing happens. This is exactly your case.
edited May 15 at 21:41
z3p5e6
31
31
answered May 15 at 15:23
Yuval FilmusYuval Filmus
200k15194358
200k15194358
3
$begingroup$
"Casting does absolutely nothing" is too broad a statement to lead with. It only does nothing for this particular cast, and only because of the signed and unsigned integer representations we've chosen. Especially for integer->floating or floating->integer casts, the new and old bit patterns in memory could be completely different. If offset binary had somehow become the standard for signed integers, unsigned->signed and signed->unsigned would change the bit pattern too.
$endgroup$
– user2357112
May 15 at 19:02
1
$begingroup$
(We don't have to worry about offset binary in our timeline, and it's forbidden by the C standard, but if things had played out differently, we could have had to deal with integer formats where the signed and unsigned representations of the same nonnegative number don't match.)
$endgroup$
– user2357112
May 15 at 19:02
$begingroup$
The main point is that datatypes are an artificial construct. Down below it's just bits. Only the program can give them meaning.
$endgroup$
– Yuval Filmus
May 15 at 19:03
1
$begingroup$
Plenty of things are misleading or inaccurate in this answer. You're describing a sort of idealized 1970s C on a typical byte-oriented machine, but modern compilers have optimizations that make the semantics of practical-C a lot more subtle, while remaining compliant with the semantics defined by the C standard. “Casting does absolutely nothing” is wrong by any perspective. Between integer types of different ranges, it can apply a modulo operation (or sometimes other effects). When floating point is involved, it can approximate.
$endgroup$
– Gilles♦
May 15 at 21:58
1
$begingroup$
(cont.) Promotion is a technical term for certain automatic conversions. (A conversion is basically an implicit cast, and a cast is basically an explicit conversion.) There is no casting between'A'
and65
: both are literals of typeint
. When casting a signed integer to an unsigned integer, the value and (on virtually all implementations) the bit pattern are preserved only if the value is non-negative.
$endgroup$
– Gilles♦
May 15 at 22:00
|
show 1 more comment
3
$begingroup$
"Casting does absolutely nothing" is too broad a statement to lead with. It only does nothing for this particular cast, and only because of the signed and unsigned integer representations we've chosen. Especially for integer->floating or floating->integer casts, the new and old bit patterns in memory could be completely different. If offset binary had somehow become the standard for signed integers, unsigned->signed and signed->unsigned would change the bit pattern too.
$endgroup$
– user2357112
May 15 at 19:02
1
$begingroup$
(We don't have to worry about offset binary in our timeline, and it's forbidden by the C standard, but if things had played out differently, we could have had to deal with integer formats where the signed and unsigned representations of the same nonnegative number don't match.)
$endgroup$
– user2357112
May 15 at 19:02
$begingroup$
The main point is that datatypes are an artificial construct. Down below it's just bits. Only the program can give them meaning.
$endgroup$
– Yuval Filmus
May 15 at 19:03
1
$begingroup$
Plenty of things are misleading or inaccurate in this answer. You're describing a sort of idealized 1970s C on a typical byte-oriented machine, but modern compilers have optimizations that make the semantics of practical-C a lot more subtle, while remaining compliant with the semantics defined by the C standard. “Casting does absolutely nothing” is wrong by any perspective. Between integer types of different ranges, it can apply a modulo operation (or sometimes other effects). When floating point is involved, it can approximate.
$endgroup$
– Gilles♦
May 15 at 21:58
1
$begingroup$
(cont.) Promotion is a technical term for certain automatic conversions. (A conversion is basically an implicit cast, and a cast is basically an explicit conversion.) There is no casting between'A'
and65
: both are literals of typeint
. When casting a signed integer to an unsigned integer, the value and (on virtually all implementations) the bit pattern are preserved only if the value is non-negative.
$endgroup$
– Gilles♦
May 15 at 22:00
3
3
$begingroup$
"Casting does absolutely nothing" is too broad a statement to lead with. It only does nothing for this particular cast, and only because of the signed and unsigned integer representations we've chosen. Especially for integer->floating or floating->integer casts, the new and old bit patterns in memory could be completely different. If offset binary had somehow become the standard for signed integers, unsigned->signed and signed->unsigned would change the bit pattern too.
$endgroup$
– user2357112
May 15 at 19:02
$begingroup$
"Casting does absolutely nothing" is too broad a statement to lead with. It only does nothing for this particular cast, and only because of the signed and unsigned integer representations we've chosen. Especially for integer->floating or floating->integer casts, the new and old bit patterns in memory could be completely different. If offset binary had somehow become the standard for signed integers, unsigned->signed and signed->unsigned would change the bit pattern too.
$endgroup$
– user2357112
May 15 at 19:02
1
1
$begingroup$
(We don't have to worry about offset binary in our timeline, and it's forbidden by the C standard, but if things had played out differently, we could have had to deal with integer formats where the signed and unsigned representations of the same nonnegative number don't match.)
$endgroup$
– user2357112
May 15 at 19:02
$begingroup$
(We don't have to worry about offset binary in our timeline, and it's forbidden by the C standard, but if things had played out differently, we could have had to deal with integer formats where the signed and unsigned representations of the same nonnegative number don't match.)
$endgroup$
– user2357112
May 15 at 19:02
$begingroup$
The main point is that datatypes are an artificial construct. Down below it's just bits. Only the program can give them meaning.
$endgroup$
– Yuval Filmus
May 15 at 19:03
$begingroup$
The main point is that datatypes are an artificial construct. Down below it's just bits. Only the program can give them meaning.
$endgroup$
– Yuval Filmus
May 15 at 19:03
1
1
$begingroup$
Plenty of things are misleading or inaccurate in this answer. You're describing a sort of idealized 1970s C on a typical byte-oriented machine, but modern compilers have optimizations that make the semantics of practical-C a lot more subtle, while remaining compliant with the semantics defined by the C standard. “Casting does absolutely nothing” is wrong by any perspective. Between integer types of different ranges, it can apply a modulo operation (or sometimes other effects). When floating point is involved, it can approximate.
$endgroup$
– Gilles♦
May 15 at 21:58
$begingroup$
Plenty of things are misleading or inaccurate in this answer. You're describing a sort of idealized 1970s C on a typical byte-oriented machine, but modern compilers have optimizations that make the semantics of practical-C a lot more subtle, while remaining compliant with the semantics defined by the C standard. “Casting does absolutely nothing” is wrong by any perspective. Between integer types of different ranges, it can apply a modulo operation (or sometimes other effects). When floating point is involved, it can approximate.
$endgroup$
– Gilles♦
May 15 at 21:58
1
1
$begingroup$
(cont.) Promotion is a technical term for certain automatic conversions. (A conversion is basically an implicit cast, and a cast is basically an explicit conversion.) There is no casting between
'A'
and 65
: both are literals of type int
. When casting a signed integer to an unsigned integer, the value and (on virtually all implementations) the bit pattern are preserved only if the value is non-negative.$endgroup$
– Gilles♦
May 15 at 22:00
$begingroup$
(cont.) Promotion is a technical term for certain automatic conversions. (A conversion is basically an implicit cast, and a cast is basically an explicit conversion.) There is no casting between
'A'
and 65
: both are literals of type int
. When casting a signed integer to an unsigned integer, the value and (on virtually all implementations) the bit pattern are preserved only if the value is non-negative.$endgroup$
– Gilles♦
May 15 at 22:00
|
show 1 more comment
$begingroup$
How does the casting of primitive datatypes works?
The restriction "primitive datatype" is not enough:
Casting from a 16-bit integer to a 32-bit integer works differently than casting between integer types of the same size. And casting between integer and floating-point types is even more complex.
This Code does work fine. What about this code?
Both code examples will work differently on different CPUs, operating systems and possibly using different compilers.
This is because under some operating systems (e.g. 32-bit Windows) long
and unsigned
data types have the same number of bits; under other operating systems (e.g. 64-bit Windows) long
has twice as many bits as unsigned
.
And you use the ~
operator (~n)
on a signed data type which may cause different results depending on the compiler you use.
This code indeed prints out negative numbers.
So is there an (architectural) way, to explain this phenomenon?
There are two effects:
The first effect is:
If you pass a value of a wrong data type to some function, C will automatically cast the value.
And if you are casting a value that is outside the range of the data type you are casting to, the result will typically not be what you are intuitively expecting.
In the case of two integer data types, normally the "rightmost" bits of the value are copied to the new value.
Let's take (signed char)3478
as an example:
The number 3478 is written as 110110010110
in binary. signed char
is an 8 bit data type. The rightmost 8 bits of 1101 10010110
are 10010110
. And the value (-106) is stored as 10010110
in a signed char
variable.
Therefore (signed char)3478
will result in (-106).
The value a
is stored in an unsigned long
variable which means that it cannot be negative!
However, you say that you "print out" the value of a
. This means you pass the value to some function that prints it to the screen.
Obviously that function expects some signed data type. C will cast the value stored in a
to the signed data type and as in the example with the number 3478, the result of casting may be a negative number.
If you use printf
you may see a second effect:
If some function (such as printf
) has a variable number of parameters, the way the parameters are passed to the function is depending on the data types passed. (The details may also vary from OS to OS.)
You may now pass the wrong data type to the function; for example you use "%d"
(signed int
) in the printf
format string but you pass an unsigned long
.
If you do this, the compiler will write the value of a
to some data storage (memory or register) which is suitable for unsigned long
values before actually calling the function. (For such functions the compiler is not able to find out which data type is really expected by the function, so it has to assume that the function expects the data type you are passing to the function.)
However, printf
will look for a value in some data storage suitable for signed int
values which might be located somewhere completely different. That data storage does not contain any useful value because the compiler wrote the value of a
somewhere else!
$endgroup$
add a comment |
$begingroup$
How does the casting of primitive datatypes works?
The restriction "primitive datatype" is not enough:
Casting from a 16-bit integer to a 32-bit integer works differently than casting between integer types of the same size. And casting between integer and floating-point types is even more complex.
This Code does work fine. What about this code?
Both code examples will work differently on different CPUs, operating systems and possibly using different compilers.
This is because under some operating systems (e.g. 32-bit Windows) long
and unsigned
data types have the same number of bits; under other operating systems (e.g. 64-bit Windows) long
has twice as many bits as unsigned
.
And you use the ~
operator (~n)
on a signed data type which may cause different results depending on the compiler you use.
This code indeed prints out negative numbers.
So is there an (architectural) way, to explain this phenomenon?
There are two effects:
The first effect is:
If you pass a value of a wrong data type to some function, C will automatically cast the value.
And if you are casting a value that is outside the range of the data type you are casting to, the result will typically not be what you are intuitively expecting.
In the case of two integer data types, normally the "rightmost" bits of the value are copied to the new value.
Let's take (signed char)3478
as an example:
The number 3478 is written as 110110010110
in binary. signed char
is an 8 bit data type. The rightmost 8 bits of 1101 10010110
are 10010110
. And the value (-106) is stored as 10010110
in a signed char
variable.
Therefore (signed char)3478
will result in (-106).
The value a
is stored in an unsigned long
variable which means that it cannot be negative!
However, you say that you "print out" the value of a
. This means you pass the value to some function that prints it to the screen.
Obviously that function expects some signed data type. C will cast the value stored in a
to the signed data type and as in the example with the number 3478, the result of casting may be a negative number.
If you use printf
you may see a second effect:
If some function (such as printf
) has a variable number of parameters, the way the parameters are passed to the function is depending on the data types passed. (The details may also vary from OS to OS.)
You may now pass the wrong data type to the function; for example you use "%d"
(signed int
) in the printf
format string but you pass an unsigned long
.
If you do this, the compiler will write the value of a
to some data storage (memory or register) which is suitable for unsigned long
values before actually calling the function. (For such functions the compiler is not able to find out which data type is really expected by the function, so it has to assume that the function expects the data type you are passing to the function.)
However, printf
will look for a value in some data storage suitable for signed int
values which might be located somewhere completely different. That data storage does not contain any useful value because the compiler wrote the value of a
somewhere else!
$endgroup$
add a comment |
$begingroup$
How does the casting of primitive datatypes works?
The restriction "primitive datatype" is not enough:
Casting from a 16-bit integer to a 32-bit integer works differently than casting between integer types of the same size. And casting between integer and floating-point types is even more complex.
This Code does work fine. What about this code?
Both code examples will work differently on different CPUs, operating systems and possibly using different compilers.
This is because under some operating systems (e.g. 32-bit Windows) long
and unsigned
data types have the same number of bits; under other operating systems (e.g. 64-bit Windows) long
has twice as many bits as unsigned
.
And you use the ~
operator (~n)
on a signed data type which may cause different results depending on the compiler you use.
This code indeed prints out negative numbers.
So is there an (architectural) way, to explain this phenomenon?
There are two effects:
The first effect is:
If you pass a value of a wrong data type to some function, C will automatically cast the value.
And if you are casting a value that is outside the range of the data type you are casting to, the result will typically not be what you are intuitively expecting.
In the case of two integer data types, normally the "rightmost" bits of the value are copied to the new value.
Let's take (signed char)3478
as an example:
The number 3478 is written as 110110010110
in binary. signed char
is an 8 bit data type. The rightmost 8 bits of 1101 10010110
are 10010110
. And the value (-106) is stored as 10010110
in a signed char
variable.
Therefore (signed char)3478
will result in (-106).
The value a
is stored in an unsigned long
variable which means that it cannot be negative!
However, you say that you "print out" the value of a
. This means you pass the value to some function that prints it to the screen.
Obviously that function expects some signed data type. C will cast the value stored in a
to the signed data type and as in the example with the number 3478, the result of casting may be a negative number.
If you use printf
you may see a second effect:
If some function (such as printf
) has a variable number of parameters, the way the parameters are passed to the function is depending on the data types passed. (The details may also vary from OS to OS.)
You may now pass the wrong data type to the function; for example you use "%d"
(signed int
) in the printf
format string but you pass an unsigned long
.
If you do this, the compiler will write the value of a
to some data storage (memory or register) which is suitable for unsigned long
values before actually calling the function. (For such functions the compiler is not able to find out which data type is really expected by the function, so it has to assume that the function expects the data type you are passing to the function.)
However, printf
will look for a value in some data storage suitable for signed int
values which might be located somewhere completely different. That data storage does not contain any useful value because the compiler wrote the value of a
somewhere else!
$endgroup$
How does the casting of primitive datatypes works?
The restriction "primitive datatype" is not enough:
Casting from a 16-bit integer to a 32-bit integer works differently than casting between integer types of the same size. And casting between integer and floating-point types is even more complex.
This Code does work fine. What about this code?
Both code examples will work differently on different CPUs, operating systems and possibly using different compilers.
This is because under some operating systems (e.g. 32-bit Windows) long
and unsigned
data types have the same number of bits; under other operating systems (e.g. 64-bit Windows) long
has twice as many bits as unsigned
.
And you use the ~
operator (~n)
on a signed data type which may cause different results depending on the compiler you use.
This code indeed prints out negative numbers.
So is there an (architectural) way, to explain this phenomenon?
There are two effects:
The first effect is:
If you pass a value of a wrong data type to some function, C will automatically cast the value.
And if you are casting a value that is outside the range of the data type you are casting to, the result will typically not be what you are intuitively expecting.
In the case of two integer data types, normally the "rightmost" bits of the value are copied to the new value.
Let's take (signed char)3478
as an example:
The number 3478 is written as 110110010110
in binary. signed char
is an 8 bit data type. The rightmost 8 bits of 1101 10010110
are 10010110
. And the value (-106) is stored as 10010110
in a signed char
variable.
Therefore (signed char)3478
will result in (-106).
The value a
is stored in an unsigned long
variable which means that it cannot be negative!
However, you say that you "print out" the value of a
. This means you pass the value to some function that prints it to the screen.
Obviously that function expects some signed data type. C will cast the value stored in a
to the signed data type and as in the example with the number 3478, the result of casting may be a negative number.
If you use printf
you may see a second effect:
If some function (such as printf
) has a variable number of parameters, the way the parameters are passed to the function is depending on the data types passed. (The details may also vary from OS to OS.)
You may now pass the wrong data type to the function; for example you use "%d"
(signed int
) in the printf
format string but you pass an unsigned long
.
If you do this, the compiler will write the value of a
to some data storage (memory or register) which is suitable for unsigned long
values before actually calling the function. (For such functions the compiler is not able to find out which data type is really expected by the function, so it has to assume that the function expects the data type you are passing to the function.)
However, printf
will look for a value in some data storage suitable for signed int
values which might be located somewhere completely different. That data storage does not contain any useful value because the compiler wrote the value of a
somewhere else!
edited May 15 at 20:09
answered May 15 at 20:04
Martin RosenauMartin Rosenau
1756
1756
add a comment |
add a comment |
5
$begingroup$
This isn’t Computer Science, but very basic C programming. But since you are here, I’d advise you to google for “C Standard Draft” and use the document you find.
$endgroup$
– gnasher729
May 15 at 15:09
1
$begingroup$
There is no print command in the second example. What do you mean by "the code indeed prints out negative numbers"?
$endgroup$
– JiK
May 15 at 19:24