Should a cast be used to truncate a long variable?
I have a 16 bits unsigned variable. I need to split it in 8 bits chunks.
Is doing the following enough:
chunk_lsb = (uint8)variable;
chunk_msb = (uint8)(variable >> 8);
Or should I use a mask:
chunk_lsb = (uint8)(variable & 0xFFu);
chunk_msb = (uint8)((variable >> 8) & 0xFFu);
I know that both approaches work, I'm just looking for the best way to do it, if there is one. Maybe there's none and just use the cast to reduce calculations is the best way? What do you guys think?
c casting mask truncate
add a comment |
I have a 16 bits unsigned variable. I need to split it in 8 bits chunks.
Is doing the following enough:
chunk_lsb = (uint8)variable;
chunk_msb = (uint8)(variable >> 8);
Or should I use a mask:
chunk_lsb = (uint8)(variable & 0xFFu);
chunk_msb = (uint8)((variable >> 8) & 0xFFu);
I know that both approaches work, I'm just looking for the best way to do it, if there is one. Maybe there's none and just use the cast to reduce calculations is the best way? What do you guys think?
c casting mask truncate
I think both will generate same binary code. As the first solution is more readable, I would use this one. You just need to be sure that uint8 is 8-but on all platforms (i guess it is)
– jaudo
Feb 25 at 12:47
Thank you @jaudo, that's also the direction I'm taking.
– Soyding Mete
Feb 25 at 12:54
11
Sidenote: Standardstdint.h
types such asuint8_t
should be preferred instead of creating your own.
– user694733
Feb 25 at 13:01
add a comment |
I have a 16 bits unsigned variable. I need to split it in 8 bits chunks.
Is doing the following enough:
chunk_lsb = (uint8)variable;
chunk_msb = (uint8)(variable >> 8);
Or should I use a mask:
chunk_lsb = (uint8)(variable & 0xFFu);
chunk_msb = (uint8)((variable >> 8) & 0xFFu);
I know that both approaches work, I'm just looking for the best way to do it, if there is one. Maybe there's none and just use the cast to reduce calculations is the best way? What do you guys think?
c casting mask truncate
I have a 16 bits unsigned variable. I need to split it in 8 bits chunks.
Is doing the following enough:
chunk_lsb = (uint8)variable;
chunk_msb = (uint8)(variable >> 8);
Or should I use a mask:
chunk_lsb = (uint8)(variable & 0xFFu);
chunk_msb = (uint8)((variable >> 8) & 0xFFu);
I know that both approaches work, I'm just looking for the best way to do it, if there is one. Maybe there's none and just use the cast to reduce calculations is the best way? What do you guys think?
c casting mask truncate
c casting mask truncate
edited Feb 25 at 12:49
Soyding Mete
asked Feb 25 at 12:41
Soyding MeteSoyding Mete
525
525
I think both will generate same binary code. As the first solution is more readable, I would use this one. You just need to be sure that uint8 is 8-but on all platforms (i guess it is)
– jaudo
Feb 25 at 12:47
Thank you @jaudo, that's also the direction I'm taking.
– Soyding Mete
Feb 25 at 12:54
11
Sidenote: Standardstdint.h
types such asuint8_t
should be preferred instead of creating your own.
– user694733
Feb 25 at 13:01
add a comment |
I think both will generate same binary code. As the first solution is more readable, I would use this one. You just need to be sure that uint8 is 8-but on all platforms (i guess it is)
– jaudo
Feb 25 at 12:47
Thank you @jaudo, that's also the direction I'm taking.
– Soyding Mete
Feb 25 at 12:54
11
Sidenote: Standardstdint.h
types such asuint8_t
should be preferred instead of creating your own.
– user694733
Feb 25 at 13:01
I think both will generate same binary code. As the first solution is more readable, I would use this one. You just need to be sure that uint8 is 8-but on all platforms (i guess it is)
– jaudo
Feb 25 at 12:47
I think both will generate same binary code. As the first solution is more readable, I would use this one. You just need to be sure that uint8 is 8-but on all platforms (i guess it is)
– jaudo
Feb 25 at 12:47
Thank you @jaudo, that's also the direction I'm taking.
– Soyding Mete
Feb 25 at 12:54
Thank you @jaudo, that's also the direction I'm taking.
– Soyding Mete
Feb 25 at 12:54
11
11
Sidenote: Standard
stdint.h
types such as uint8_t
should be preferred instead of creating your own.– user694733
Feb 25 at 13:01
Sidenote: Standard
stdint.h
types such as uint8_t
should be preferred instead of creating your own.– user694733
Feb 25 at 13:01
add a comment |
4 Answers
4
active
oldest
votes
It isn't clear what type variable
is. Without that specified, we can only speculate.
But in general, you should avoid bit shifting on signed integer types, as that leads to various forms of poorly-defined behavior. This in turn means that you have to be careful with small integer types too, because they get promoted to signed int
. See Implicit type promotion rules.
The specific case of (uint8)((variable >> 8) & 0xFFu);
is safe if variable
is unsigned. Otherwise it is unsafe, since right-shifting a negative value leads to implementation-defined behavior (arithmetic or logical shift).
variable << 8
will invoke undefined behavior on 16 bit systems in case variable
is a small integer type, or an int16_t
.
The safest, most portable way no matter left/right shift is therefore this:
chunk_lsb = variable;
chunk_msb = ((unsigned int)variable >> 8);
Though you might want to be overly explicit in order to silence all compiler warnings:
chunk_lsb = (uint8_t) (variable & 0xFFu);
chunk_msb = (uint8_t) ( (unsigned int)variable>>8 & 0xFFu );
AFAICT(variable >> 8) & 0xFFu
will give the same result regardless of whether the shift is arithmetic or logical, as long asint
is at least 16 bits wide (which I believe the standard guarantees it to be).
– Ilmari Karonen
Feb 25 at 15:37
1
@IlmariKaronen The size of the variable doesn't matter for that, only the signedness. In case the value is negative, you get implementation-defined behavior. One example of such broken code would beunsigned char u = 0; ... ~u >> n;
– Lundin
Feb 25 at 15:41
I agree that it's unpredictable, insofar as the standard doesn't require the implementation-defined result of right-shifting a negative signed integer to be either an arithmetic or a logical shift. However, on implementations where it is one of those two, shifting right by n bits and then masking off (at least) the n highest bits should produce the same result regardless of which kind of shift it is. Thus, your parenthetical remark in the third paragraph of your answer seems a bit misleading.
– Ilmari Karonen
Feb 25 at 16:18
@Lundin, it is perhaps worthwhile to note that your first set of recommendations assumes thatchunk_lsb
andchunk_msb
themselves have type equivalent touint8_t
. That isn't clear from the question -- the OP's "8 bits chunks" could be stored in variables having a wider type, or a signed one.
– John Bollinger
Feb 25 at 16:29
1
Disagree about using(uint8_t)
cast and the mask inchunk_lsb = (uint8_t) (variable & 0xFFu);
. The cast is always sufficient to silence warnings. The mask is better programming. But both is WET. Aside from that I really like the answer.
– chux
Feb 26 at 2:07
|
show 3 more comments
Since uint8
is unsigned, you don't have to do the masking:
6.3.1.3 Signed and unsigned integers
- 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.
- 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. 60)
- 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.
However, most likely both will result in the same compiler output. I usually add the mask because it makes clear what code is supposed to do, and makes the cast unnecessary.
1
Except, C has nasty integer promotion, so if you try to do the same thing and left shift, the program may implode in undefined behavior. In this particular case we get away since it is right shift of a positive number.
– Lundin
Feb 25 at 13:04
Also, the cast makes the loss of precision clearly intentional. Many compilers can emit warning on loss of precision, which can extremely important. (I'm thinking of noobs who are so certain that "size_t
and pointers are really justunsigned int
...)
– Andrew Henle
Feb 25 at 13:08
@Lundin: Under C89, signed left-shift behavior was defined for all possible left-operand values on implementations whose integer types don't have padding bits or trap representations, but trapping might have been more sensible in situations where the mandated behavior would differ from that of power-of-two multiplication. I've seen no evidence to suggest any intention to change the handling of situations where C89's behavior and power-of-two multiplication would be equivalent.
– supercat
Feb 25 at 17:44
@supercat It's bad enough to accidentally shift data into the sign bit, regardless of UB and signedness format.
– Lundin
Feb 25 at 19:16
@Lundin: In two's-complement notation, the sign bit is simply shorthand for "the state of this bit and an infinite number of bits to the left". The value -3 is an infinite number of ones followed by 01. It may be more convenient to think of a 32-bit two's-complement value as being composed of an infinite number of 0's or 1's followed by exactly 31 more bits, but an infinite number of 1's, followed by 29 more, and then 01, is the same as an infinite number of 1's followed by 01.
– supercat
Feb 25 at 19:42
|
show 3 more comments
Should a cast be used to truncate a long variable?
If chunk_lsb
is an 8 bit object (narrower than variable
), use cast or mask (not both). Useful in quieting pedantic warnings about range reduction. I prefer the mask - unless the compiler is picky.
uint8_t chunk_lsb = (uint8_t) variable;
// or
uint8_t chunk_lsb = variable & 0xFFu;
Otherwise use the mask.
unsigned chunk_lsb = variable & 0xFFu;
This and your comment about using both being a WET solution is satisfying thank you @chux
– Soyding Mete
Feb 26 at 7:31
add a comment |
Maybe there's none and just use the cast to reduce calculations is the best way?
General speaking, the asm code will be the same, so in terms of speed, it does not matter which one you use:
- masking: https://godbolt.org/z/Olrw3x
- casting: https://godbolt.org/z/EEdvQZ
What do you guys think?
IMO, the first one is clearer, in terms of readability, but I cannot figure out a coding standard or guideline which supports my preference. Anyway, I would use a const
variable if your preference is the second one, to remove magic numbers and make clearer that the purpose is masking (assuming that you have chosen a correct name for the const variable).
3
Anyway, I would use aconst
variable if your preference is the second one, to remove magic numbers and make clearer that the purpose is masking Anyone who thinks0xFF
in the context of bit masking is a "magic number" that has to be papered over with a variable is engaging in cargo-cult programming. What possible variable can be more clear than0xFF
as a bit mask?one_byte_bit_mask_0xFF
?
– Andrew Henle
Feb 25 at 13:05
IMO, if the purpose is to mask, the word mask is clearer than any (magic) number.
– Jose
Feb 25 at 13:44
IMO, if the purpose is to mask, the word mask is clearer than any (magic) number Oh? How many bits does this mask:unsigned int result = input & mask;
And no, I didn't DV.
– Andrew Henle
Feb 25 at 13:57
0xFF
vsone_byte_bit_mask_0xFF
vsmask
, I choose the second one as the least-bad option.
– Jose
Feb 25 at 14:04
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54866461%2fshould-a-cast-be-used-to-truncate-a-long-variable%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
It isn't clear what type variable
is. Without that specified, we can only speculate.
But in general, you should avoid bit shifting on signed integer types, as that leads to various forms of poorly-defined behavior. This in turn means that you have to be careful with small integer types too, because they get promoted to signed int
. See Implicit type promotion rules.
The specific case of (uint8)((variable >> 8) & 0xFFu);
is safe if variable
is unsigned. Otherwise it is unsafe, since right-shifting a negative value leads to implementation-defined behavior (arithmetic or logical shift).
variable << 8
will invoke undefined behavior on 16 bit systems in case variable
is a small integer type, or an int16_t
.
The safest, most portable way no matter left/right shift is therefore this:
chunk_lsb = variable;
chunk_msb = ((unsigned int)variable >> 8);
Though you might want to be overly explicit in order to silence all compiler warnings:
chunk_lsb = (uint8_t) (variable & 0xFFu);
chunk_msb = (uint8_t) ( (unsigned int)variable>>8 & 0xFFu );
AFAICT(variable >> 8) & 0xFFu
will give the same result regardless of whether the shift is arithmetic or logical, as long asint
is at least 16 bits wide (which I believe the standard guarantees it to be).
– Ilmari Karonen
Feb 25 at 15:37
1
@IlmariKaronen The size of the variable doesn't matter for that, only the signedness. In case the value is negative, you get implementation-defined behavior. One example of such broken code would beunsigned char u = 0; ... ~u >> n;
– Lundin
Feb 25 at 15:41
I agree that it's unpredictable, insofar as the standard doesn't require the implementation-defined result of right-shifting a negative signed integer to be either an arithmetic or a logical shift. However, on implementations where it is one of those two, shifting right by n bits and then masking off (at least) the n highest bits should produce the same result regardless of which kind of shift it is. Thus, your parenthetical remark in the third paragraph of your answer seems a bit misleading.
– Ilmari Karonen
Feb 25 at 16:18
@Lundin, it is perhaps worthwhile to note that your first set of recommendations assumes thatchunk_lsb
andchunk_msb
themselves have type equivalent touint8_t
. That isn't clear from the question -- the OP's "8 bits chunks" could be stored in variables having a wider type, or a signed one.
– John Bollinger
Feb 25 at 16:29
1
Disagree about using(uint8_t)
cast and the mask inchunk_lsb = (uint8_t) (variable & 0xFFu);
. The cast is always sufficient to silence warnings. The mask is better programming. But both is WET. Aside from that I really like the answer.
– chux
Feb 26 at 2:07
|
show 3 more comments
It isn't clear what type variable
is. Without that specified, we can only speculate.
But in general, you should avoid bit shifting on signed integer types, as that leads to various forms of poorly-defined behavior. This in turn means that you have to be careful with small integer types too, because they get promoted to signed int
. See Implicit type promotion rules.
The specific case of (uint8)((variable >> 8) & 0xFFu);
is safe if variable
is unsigned. Otherwise it is unsafe, since right-shifting a negative value leads to implementation-defined behavior (arithmetic or logical shift).
variable << 8
will invoke undefined behavior on 16 bit systems in case variable
is a small integer type, or an int16_t
.
The safest, most portable way no matter left/right shift is therefore this:
chunk_lsb = variable;
chunk_msb = ((unsigned int)variable >> 8);
Though you might want to be overly explicit in order to silence all compiler warnings:
chunk_lsb = (uint8_t) (variable & 0xFFu);
chunk_msb = (uint8_t) ( (unsigned int)variable>>8 & 0xFFu );
AFAICT(variable >> 8) & 0xFFu
will give the same result regardless of whether the shift is arithmetic or logical, as long asint
is at least 16 bits wide (which I believe the standard guarantees it to be).
– Ilmari Karonen
Feb 25 at 15:37
1
@IlmariKaronen The size of the variable doesn't matter for that, only the signedness. In case the value is negative, you get implementation-defined behavior. One example of such broken code would beunsigned char u = 0; ... ~u >> n;
– Lundin
Feb 25 at 15:41
I agree that it's unpredictable, insofar as the standard doesn't require the implementation-defined result of right-shifting a negative signed integer to be either an arithmetic or a logical shift. However, on implementations where it is one of those two, shifting right by n bits and then masking off (at least) the n highest bits should produce the same result regardless of which kind of shift it is. Thus, your parenthetical remark in the third paragraph of your answer seems a bit misleading.
– Ilmari Karonen
Feb 25 at 16:18
@Lundin, it is perhaps worthwhile to note that your first set of recommendations assumes thatchunk_lsb
andchunk_msb
themselves have type equivalent touint8_t
. That isn't clear from the question -- the OP's "8 bits chunks" could be stored in variables having a wider type, or a signed one.
– John Bollinger
Feb 25 at 16:29
1
Disagree about using(uint8_t)
cast and the mask inchunk_lsb = (uint8_t) (variable & 0xFFu);
. The cast is always sufficient to silence warnings. The mask is better programming. But both is WET. Aside from that I really like the answer.
– chux
Feb 26 at 2:07
|
show 3 more comments
It isn't clear what type variable
is. Without that specified, we can only speculate.
But in general, you should avoid bit shifting on signed integer types, as that leads to various forms of poorly-defined behavior. This in turn means that you have to be careful with small integer types too, because they get promoted to signed int
. See Implicit type promotion rules.
The specific case of (uint8)((variable >> 8) & 0xFFu);
is safe if variable
is unsigned. Otherwise it is unsafe, since right-shifting a negative value leads to implementation-defined behavior (arithmetic or logical shift).
variable << 8
will invoke undefined behavior on 16 bit systems in case variable
is a small integer type, or an int16_t
.
The safest, most portable way no matter left/right shift is therefore this:
chunk_lsb = variable;
chunk_msb = ((unsigned int)variable >> 8);
Though you might want to be overly explicit in order to silence all compiler warnings:
chunk_lsb = (uint8_t) (variable & 0xFFu);
chunk_msb = (uint8_t) ( (unsigned int)variable>>8 & 0xFFu );
It isn't clear what type variable
is. Without that specified, we can only speculate.
But in general, you should avoid bit shifting on signed integer types, as that leads to various forms of poorly-defined behavior. This in turn means that you have to be careful with small integer types too, because they get promoted to signed int
. See Implicit type promotion rules.
The specific case of (uint8)((variable >> 8) & 0xFFu);
is safe if variable
is unsigned. Otherwise it is unsafe, since right-shifting a negative value leads to implementation-defined behavior (arithmetic or logical shift).
variable << 8
will invoke undefined behavior on 16 bit systems in case variable
is a small integer type, or an int16_t
.
The safest, most portable way no matter left/right shift is therefore this:
chunk_lsb = variable;
chunk_msb = ((unsigned int)variable >> 8);
Though you might want to be overly explicit in order to silence all compiler warnings:
chunk_lsb = (uint8_t) (variable & 0xFFu);
chunk_msb = (uint8_t) ( (unsigned int)variable>>8 & 0xFFu );
answered Feb 25 at 13:50
LundinLundin
111k17161267
111k17161267
AFAICT(variable >> 8) & 0xFFu
will give the same result regardless of whether the shift is arithmetic or logical, as long asint
is at least 16 bits wide (which I believe the standard guarantees it to be).
– Ilmari Karonen
Feb 25 at 15:37
1
@IlmariKaronen The size of the variable doesn't matter for that, only the signedness. In case the value is negative, you get implementation-defined behavior. One example of such broken code would beunsigned char u = 0; ... ~u >> n;
– Lundin
Feb 25 at 15:41
I agree that it's unpredictable, insofar as the standard doesn't require the implementation-defined result of right-shifting a negative signed integer to be either an arithmetic or a logical shift. However, on implementations where it is one of those two, shifting right by n bits and then masking off (at least) the n highest bits should produce the same result regardless of which kind of shift it is. Thus, your parenthetical remark in the third paragraph of your answer seems a bit misleading.
– Ilmari Karonen
Feb 25 at 16:18
@Lundin, it is perhaps worthwhile to note that your first set of recommendations assumes thatchunk_lsb
andchunk_msb
themselves have type equivalent touint8_t
. That isn't clear from the question -- the OP's "8 bits chunks" could be stored in variables having a wider type, or a signed one.
– John Bollinger
Feb 25 at 16:29
1
Disagree about using(uint8_t)
cast and the mask inchunk_lsb = (uint8_t) (variable & 0xFFu);
. The cast is always sufficient to silence warnings. The mask is better programming. But both is WET. Aside from that I really like the answer.
– chux
Feb 26 at 2:07
|
show 3 more comments
AFAICT(variable >> 8) & 0xFFu
will give the same result regardless of whether the shift is arithmetic or logical, as long asint
is at least 16 bits wide (which I believe the standard guarantees it to be).
– Ilmari Karonen
Feb 25 at 15:37
1
@IlmariKaronen The size of the variable doesn't matter for that, only the signedness. In case the value is negative, you get implementation-defined behavior. One example of such broken code would beunsigned char u = 0; ... ~u >> n;
– Lundin
Feb 25 at 15:41
I agree that it's unpredictable, insofar as the standard doesn't require the implementation-defined result of right-shifting a negative signed integer to be either an arithmetic or a logical shift. However, on implementations where it is one of those two, shifting right by n bits and then masking off (at least) the n highest bits should produce the same result regardless of which kind of shift it is. Thus, your parenthetical remark in the third paragraph of your answer seems a bit misleading.
– Ilmari Karonen
Feb 25 at 16:18
@Lundin, it is perhaps worthwhile to note that your first set of recommendations assumes thatchunk_lsb
andchunk_msb
themselves have type equivalent touint8_t
. That isn't clear from the question -- the OP's "8 bits chunks" could be stored in variables having a wider type, or a signed one.
– John Bollinger
Feb 25 at 16:29
1
Disagree about using(uint8_t)
cast and the mask inchunk_lsb = (uint8_t) (variable & 0xFFu);
. The cast is always sufficient to silence warnings. The mask is better programming. But both is WET. Aside from that I really like the answer.
– chux
Feb 26 at 2:07
AFAICT
(variable >> 8) & 0xFFu
will give the same result regardless of whether the shift is arithmetic or logical, as long as int
is at least 16 bits wide (which I believe the standard guarantees it to be).– Ilmari Karonen
Feb 25 at 15:37
AFAICT
(variable >> 8) & 0xFFu
will give the same result regardless of whether the shift is arithmetic or logical, as long as int
is at least 16 bits wide (which I believe the standard guarantees it to be).– Ilmari Karonen
Feb 25 at 15:37
1
1
@IlmariKaronen The size of the variable doesn't matter for that, only the signedness. In case the value is negative, you get implementation-defined behavior. One example of such broken code would be
unsigned char u = 0; ... ~u >> n;
– Lundin
Feb 25 at 15:41
@IlmariKaronen The size of the variable doesn't matter for that, only the signedness. In case the value is negative, you get implementation-defined behavior. One example of such broken code would be
unsigned char u = 0; ... ~u >> n;
– Lundin
Feb 25 at 15:41
I agree that it's unpredictable, insofar as the standard doesn't require the implementation-defined result of right-shifting a negative signed integer to be either an arithmetic or a logical shift. However, on implementations where it is one of those two, shifting right by n bits and then masking off (at least) the n highest bits should produce the same result regardless of which kind of shift it is. Thus, your parenthetical remark in the third paragraph of your answer seems a bit misleading.
– Ilmari Karonen
Feb 25 at 16:18
I agree that it's unpredictable, insofar as the standard doesn't require the implementation-defined result of right-shifting a negative signed integer to be either an arithmetic or a logical shift. However, on implementations where it is one of those two, shifting right by n bits and then masking off (at least) the n highest bits should produce the same result regardless of which kind of shift it is. Thus, your parenthetical remark in the third paragraph of your answer seems a bit misleading.
– Ilmari Karonen
Feb 25 at 16:18
@Lundin, it is perhaps worthwhile to note that your first set of recommendations assumes that
chunk_lsb
and chunk_msb
themselves have type equivalent to uint8_t
. That isn't clear from the question -- the OP's "8 bits chunks" could be stored in variables having a wider type, or a signed one.– John Bollinger
Feb 25 at 16:29
@Lundin, it is perhaps worthwhile to note that your first set of recommendations assumes that
chunk_lsb
and chunk_msb
themselves have type equivalent to uint8_t
. That isn't clear from the question -- the OP's "8 bits chunks" could be stored in variables having a wider type, or a signed one.– John Bollinger
Feb 25 at 16:29
1
1
Disagree about using
(uint8_t)
cast and the mask in chunk_lsb = (uint8_t) (variable & 0xFFu);
. The cast is always sufficient to silence warnings. The mask is better programming. But both is WET. Aside from that I really like the answer.– chux
Feb 26 at 2:07
Disagree about using
(uint8_t)
cast and the mask in chunk_lsb = (uint8_t) (variable & 0xFFu);
. The cast is always sufficient to silence warnings. The mask is better programming. But both is WET. Aside from that I really like the answer.– chux
Feb 26 at 2:07
|
show 3 more comments
Since uint8
is unsigned, you don't have to do the masking:
6.3.1.3 Signed and unsigned integers
- 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.
- 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. 60)
- 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.
However, most likely both will result in the same compiler output. I usually add the mask because it makes clear what code is supposed to do, and makes the cast unnecessary.
1
Except, C has nasty integer promotion, so if you try to do the same thing and left shift, the program may implode in undefined behavior. In this particular case we get away since it is right shift of a positive number.
– Lundin
Feb 25 at 13:04
Also, the cast makes the loss of precision clearly intentional. Many compilers can emit warning on loss of precision, which can extremely important. (I'm thinking of noobs who are so certain that "size_t
and pointers are really justunsigned int
...)
– Andrew Henle
Feb 25 at 13:08
@Lundin: Under C89, signed left-shift behavior was defined for all possible left-operand values on implementations whose integer types don't have padding bits or trap representations, but trapping might have been more sensible in situations where the mandated behavior would differ from that of power-of-two multiplication. I've seen no evidence to suggest any intention to change the handling of situations where C89's behavior and power-of-two multiplication would be equivalent.
– supercat
Feb 25 at 17:44
@supercat It's bad enough to accidentally shift data into the sign bit, regardless of UB and signedness format.
– Lundin
Feb 25 at 19:16
@Lundin: In two's-complement notation, the sign bit is simply shorthand for "the state of this bit and an infinite number of bits to the left". The value -3 is an infinite number of ones followed by 01. It may be more convenient to think of a 32-bit two's-complement value as being composed of an infinite number of 0's or 1's followed by exactly 31 more bits, but an infinite number of 1's, followed by 29 more, and then 01, is the same as an infinite number of 1's followed by 01.
– supercat
Feb 25 at 19:42
|
show 3 more comments
Since uint8
is unsigned, you don't have to do the masking:
6.3.1.3 Signed and unsigned integers
- 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.
- 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. 60)
- 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.
However, most likely both will result in the same compiler output. I usually add the mask because it makes clear what code is supposed to do, and makes the cast unnecessary.
1
Except, C has nasty integer promotion, so if you try to do the same thing and left shift, the program may implode in undefined behavior. In this particular case we get away since it is right shift of a positive number.
– Lundin
Feb 25 at 13:04
Also, the cast makes the loss of precision clearly intentional. Many compilers can emit warning on loss of precision, which can extremely important. (I'm thinking of noobs who are so certain that "size_t
and pointers are really justunsigned int
...)
– Andrew Henle
Feb 25 at 13:08
@Lundin: Under C89, signed left-shift behavior was defined for all possible left-operand values on implementations whose integer types don't have padding bits or trap representations, but trapping might have been more sensible in situations where the mandated behavior would differ from that of power-of-two multiplication. I've seen no evidence to suggest any intention to change the handling of situations where C89's behavior and power-of-two multiplication would be equivalent.
– supercat
Feb 25 at 17:44
@supercat It's bad enough to accidentally shift data into the sign bit, regardless of UB and signedness format.
– Lundin
Feb 25 at 19:16
@Lundin: In two's-complement notation, the sign bit is simply shorthand for "the state of this bit and an infinite number of bits to the left". The value -3 is an infinite number of ones followed by 01. It may be more convenient to think of a 32-bit two's-complement value as being composed of an infinite number of 0's or 1's followed by exactly 31 more bits, but an infinite number of 1's, followed by 29 more, and then 01, is the same as an infinite number of 1's followed by 01.
– supercat
Feb 25 at 19:42
|
show 3 more comments
Since uint8
is unsigned, you don't have to do the masking:
6.3.1.3 Signed and unsigned integers
- 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.
- 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. 60)
- 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.
However, most likely both will result in the same compiler output. I usually add the mask because it makes clear what code is supposed to do, and makes the cast unnecessary.
Since uint8
is unsigned, you don't have to do the masking:
6.3.1.3 Signed and unsigned integers
- 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.
- 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. 60)
- 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.
However, most likely both will result in the same compiler output. I usually add the mask because it makes clear what code is supposed to do, and makes the cast unnecessary.
answered Feb 25 at 12:59
user694733user694733
11.1k12851
11.1k12851
1
Except, C has nasty integer promotion, so if you try to do the same thing and left shift, the program may implode in undefined behavior. In this particular case we get away since it is right shift of a positive number.
– Lundin
Feb 25 at 13:04
Also, the cast makes the loss of precision clearly intentional. Many compilers can emit warning on loss of precision, which can extremely important. (I'm thinking of noobs who are so certain that "size_t
and pointers are really justunsigned int
...)
– Andrew Henle
Feb 25 at 13:08
@Lundin: Under C89, signed left-shift behavior was defined for all possible left-operand values on implementations whose integer types don't have padding bits or trap representations, but trapping might have been more sensible in situations where the mandated behavior would differ from that of power-of-two multiplication. I've seen no evidence to suggest any intention to change the handling of situations where C89's behavior and power-of-two multiplication would be equivalent.
– supercat
Feb 25 at 17:44
@supercat It's bad enough to accidentally shift data into the sign bit, regardless of UB and signedness format.
– Lundin
Feb 25 at 19:16
@Lundin: In two's-complement notation, the sign bit is simply shorthand for "the state of this bit and an infinite number of bits to the left". The value -3 is an infinite number of ones followed by 01. It may be more convenient to think of a 32-bit two's-complement value as being composed of an infinite number of 0's or 1's followed by exactly 31 more bits, but an infinite number of 1's, followed by 29 more, and then 01, is the same as an infinite number of 1's followed by 01.
– supercat
Feb 25 at 19:42
|
show 3 more comments
1
Except, C has nasty integer promotion, so if you try to do the same thing and left shift, the program may implode in undefined behavior. In this particular case we get away since it is right shift of a positive number.
– Lundin
Feb 25 at 13:04
Also, the cast makes the loss of precision clearly intentional. Many compilers can emit warning on loss of precision, which can extremely important. (I'm thinking of noobs who are so certain that "size_t
and pointers are really justunsigned int
...)
– Andrew Henle
Feb 25 at 13:08
@Lundin: Under C89, signed left-shift behavior was defined for all possible left-operand values on implementations whose integer types don't have padding bits or trap representations, but trapping might have been more sensible in situations where the mandated behavior would differ from that of power-of-two multiplication. I've seen no evidence to suggest any intention to change the handling of situations where C89's behavior and power-of-two multiplication would be equivalent.
– supercat
Feb 25 at 17:44
@supercat It's bad enough to accidentally shift data into the sign bit, regardless of UB and signedness format.
– Lundin
Feb 25 at 19:16
@Lundin: In two's-complement notation, the sign bit is simply shorthand for "the state of this bit and an infinite number of bits to the left". The value -3 is an infinite number of ones followed by 01. It may be more convenient to think of a 32-bit two's-complement value as being composed of an infinite number of 0's or 1's followed by exactly 31 more bits, but an infinite number of 1's, followed by 29 more, and then 01, is the same as an infinite number of 1's followed by 01.
– supercat
Feb 25 at 19:42
1
1
Except, C has nasty integer promotion, so if you try to do the same thing and left shift, the program may implode in undefined behavior. In this particular case we get away since it is right shift of a positive number.
– Lundin
Feb 25 at 13:04
Except, C has nasty integer promotion, so if you try to do the same thing and left shift, the program may implode in undefined behavior. In this particular case we get away since it is right shift of a positive number.
– Lundin
Feb 25 at 13:04
Also, the cast makes the loss of precision clearly intentional. Many compilers can emit warning on loss of precision, which can extremely important. (I'm thinking of noobs who are so certain that "
size_t
and pointers are really just unsigned int
...)– Andrew Henle
Feb 25 at 13:08
Also, the cast makes the loss of precision clearly intentional. Many compilers can emit warning on loss of precision, which can extremely important. (I'm thinking of noobs who are so certain that "
size_t
and pointers are really just unsigned int
...)– Andrew Henle
Feb 25 at 13:08
@Lundin: Under C89, signed left-shift behavior was defined for all possible left-operand values on implementations whose integer types don't have padding bits or trap representations, but trapping might have been more sensible in situations where the mandated behavior would differ from that of power-of-two multiplication. I've seen no evidence to suggest any intention to change the handling of situations where C89's behavior and power-of-two multiplication would be equivalent.
– supercat
Feb 25 at 17:44
@Lundin: Under C89, signed left-shift behavior was defined for all possible left-operand values on implementations whose integer types don't have padding bits or trap representations, but trapping might have been more sensible in situations where the mandated behavior would differ from that of power-of-two multiplication. I've seen no evidence to suggest any intention to change the handling of situations where C89's behavior and power-of-two multiplication would be equivalent.
– supercat
Feb 25 at 17:44
@supercat It's bad enough to accidentally shift data into the sign bit, regardless of UB and signedness format.
– Lundin
Feb 25 at 19:16
@supercat It's bad enough to accidentally shift data into the sign bit, regardless of UB and signedness format.
– Lundin
Feb 25 at 19:16
@Lundin: In two's-complement notation, the sign bit is simply shorthand for "the state of this bit and an infinite number of bits to the left". The value -3 is an infinite number of ones followed by 01. It may be more convenient to think of a 32-bit two's-complement value as being composed of an infinite number of 0's or 1's followed by exactly 31 more bits, but an infinite number of 1's, followed by 29 more, and then 01, is the same as an infinite number of 1's followed by 01.
– supercat
Feb 25 at 19:42
@Lundin: In two's-complement notation, the sign bit is simply shorthand for "the state of this bit and an infinite number of bits to the left". The value -3 is an infinite number of ones followed by 01. It may be more convenient to think of a 32-bit two's-complement value as being composed of an infinite number of 0's or 1's followed by exactly 31 more bits, but an infinite number of 1's, followed by 29 more, and then 01, is the same as an infinite number of 1's followed by 01.
– supercat
Feb 25 at 19:42
|
show 3 more comments
Should a cast be used to truncate a long variable?
If chunk_lsb
is an 8 bit object (narrower than variable
), use cast or mask (not both). Useful in quieting pedantic warnings about range reduction. I prefer the mask - unless the compiler is picky.
uint8_t chunk_lsb = (uint8_t) variable;
// or
uint8_t chunk_lsb = variable & 0xFFu;
Otherwise use the mask.
unsigned chunk_lsb = variable & 0xFFu;
This and your comment about using both being a WET solution is satisfying thank you @chux
– Soyding Mete
Feb 26 at 7:31
add a comment |
Should a cast be used to truncate a long variable?
If chunk_lsb
is an 8 bit object (narrower than variable
), use cast or mask (not both). Useful in quieting pedantic warnings about range reduction. I prefer the mask - unless the compiler is picky.
uint8_t chunk_lsb = (uint8_t) variable;
// or
uint8_t chunk_lsb = variable & 0xFFu;
Otherwise use the mask.
unsigned chunk_lsb = variable & 0xFFu;
This and your comment about using both being a WET solution is satisfying thank you @chux
– Soyding Mete
Feb 26 at 7:31
add a comment |
Should a cast be used to truncate a long variable?
If chunk_lsb
is an 8 bit object (narrower than variable
), use cast or mask (not both). Useful in quieting pedantic warnings about range reduction. I prefer the mask - unless the compiler is picky.
uint8_t chunk_lsb = (uint8_t) variable;
// or
uint8_t chunk_lsb = variable & 0xFFu;
Otherwise use the mask.
unsigned chunk_lsb = variable & 0xFFu;
Should a cast be used to truncate a long variable?
If chunk_lsb
is an 8 bit object (narrower than variable
), use cast or mask (not both). Useful in quieting pedantic warnings about range reduction. I prefer the mask - unless the compiler is picky.
uint8_t chunk_lsb = (uint8_t) variable;
// or
uint8_t chunk_lsb = variable & 0xFFu;
Otherwise use the mask.
unsigned chunk_lsb = variable & 0xFFu;
edited Feb 25 at 15:06
answered Feb 25 at 13:21
chuxchux
83.6k872153
83.6k872153
This and your comment about using both being a WET solution is satisfying thank you @chux
– Soyding Mete
Feb 26 at 7:31
add a comment |
This and your comment about using both being a WET solution is satisfying thank you @chux
– Soyding Mete
Feb 26 at 7:31
This and your comment about using both being a WET solution is satisfying thank you @chux
– Soyding Mete
Feb 26 at 7:31
This and your comment about using both being a WET solution is satisfying thank you @chux
– Soyding Mete
Feb 26 at 7:31
add a comment |
Maybe there's none and just use the cast to reduce calculations is the best way?
General speaking, the asm code will be the same, so in terms of speed, it does not matter which one you use:
- masking: https://godbolt.org/z/Olrw3x
- casting: https://godbolt.org/z/EEdvQZ
What do you guys think?
IMO, the first one is clearer, in terms of readability, but I cannot figure out a coding standard or guideline which supports my preference. Anyway, I would use a const
variable if your preference is the second one, to remove magic numbers and make clearer that the purpose is masking (assuming that you have chosen a correct name for the const variable).
3
Anyway, I would use aconst
variable if your preference is the second one, to remove magic numbers and make clearer that the purpose is masking Anyone who thinks0xFF
in the context of bit masking is a "magic number" that has to be papered over with a variable is engaging in cargo-cult programming. What possible variable can be more clear than0xFF
as a bit mask?one_byte_bit_mask_0xFF
?
– Andrew Henle
Feb 25 at 13:05
IMO, if the purpose is to mask, the word mask is clearer than any (magic) number.
– Jose
Feb 25 at 13:44
IMO, if the purpose is to mask, the word mask is clearer than any (magic) number Oh? How many bits does this mask:unsigned int result = input & mask;
And no, I didn't DV.
– Andrew Henle
Feb 25 at 13:57
0xFF
vsone_byte_bit_mask_0xFF
vsmask
, I choose the second one as the least-bad option.
– Jose
Feb 25 at 14:04
add a comment |
Maybe there's none and just use the cast to reduce calculations is the best way?
General speaking, the asm code will be the same, so in terms of speed, it does not matter which one you use:
- masking: https://godbolt.org/z/Olrw3x
- casting: https://godbolt.org/z/EEdvQZ
What do you guys think?
IMO, the first one is clearer, in terms of readability, but I cannot figure out a coding standard or guideline which supports my preference. Anyway, I would use a const
variable if your preference is the second one, to remove magic numbers and make clearer that the purpose is masking (assuming that you have chosen a correct name for the const variable).
3
Anyway, I would use aconst
variable if your preference is the second one, to remove magic numbers and make clearer that the purpose is masking Anyone who thinks0xFF
in the context of bit masking is a "magic number" that has to be papered over with a variable is engaging in cargo-cult programming. What possible variable can be more clear than0xFF
as a bit mask?one_byte_bit_mask_0xFF
?
– Andrew Henle
Feb 25 at 13:05
IMO, if the purpose is to mask, the word mask is clearer than any (magic) number.
– Jose
Feb 25 at 13:44
IMO, if the purpose is to mask, the word mask is clearer than any (magic) number Oh? How many bits does this mask:unsigned int result = input & mask;
And no, I didn't DV.
– Andrew Henle
Feb 25 at 13:57
0xFF
vsone_byte_bit_mask_0xFF
vsmask
, I choose the second one as the least-bad option.
– Jose
Feb 25 at 14:04
add a comment |
Maybe there's none and just use the cast to reduce calculations is the best way?
General speaking, the asm code will be the same, so in terms of speed, it does not matter which one you use:
- masking: https://godbolt.org/z/Olrw3x
- casting: https://godbolt.org/z/EEdvQZ
What do you guys think?
IMO, the first one is clearer, in terms of readability, but I cannot figure out a coding standard or guideline which supports my preference. Anyway, I would use a const
variable if your preference is the second one, to remove magic numbers and make clearer that the purpose is masking (assuming that you have chosen a correct name for the const variable).
Maybe there's none and just use the cast to reduce calculations is the best way?
General speaking, the asm code will be the same, so in terms of speed, it does not matter which one you use:
- masking: https://godbolt.org/z/Olrw3x
- casting: https://godbolt.org/z/EEdvQZ
What do you guys think?
IMO, the first one is clearer, in terms of readability, but I cannot figure out a coding standard or guideline which supports my preference. Anyway, I would use a const
variable if your preference is the second one, to remove magic numbers and make clearer that the purpose is masking (assuming that you have chosen a correct name for the const variable).
answered Feb 25 at 12:58
JoseJose
1,241415
1,241415
3
Anyway, I would use aconst
variable if your preference is the second one, to remove magic numbers and make clearer that the purpose is masking Anyone who thinks0xFF
in the context of bit masking is a "magic number" that has to be papered over with a variable is engaging in cargo-cult programming. What possible variable can be more clear than0xFF
as a bit mask?one_byte_bit_mask_0xFF
?
– Andrew Henle
Feb 25 at 13:05
IMO, if the purpose is to mask, the word mask is clearer than any (magic) number.
– Jose
Feb 25 at 13:44
IMO, if the purpose is to mask, the word mask is clearer than any (magic) number Oh? How many bits does this mask:unsigned int result = input & mask;
And no, I didn't DV.
– Andrew Henle
Feb 25 at 13:57
0xFF
vsone_byte_bit_mask_0xFF
vsmask
, I choose the second one as the least-bad option.
– Jose
Feb 25 at 14:04
add a comment |
3
Anyway, I would use aconst
variable if your preference is the second one, to remove magic numbers and make clearer that the purpose is masking Anyone who thinks0xFF
in the context of bit masking is a "magic number" that has to be papered over with a variable is engaging in cargo-cult programming. What possible variable can be more clear than0xFF
as a bit mask?one_byte_bit_mask_0xFF
?
– Andrew Henle
Feb 25 at 13:05
IMO, if the purpose is to mask, the word mask is clearer than any (magic) number.
– Jose
Feb 25 at 13:44
IMO, if the purpose is to mask, the word mask is clearer than any (magic) number Oh? How many bits does this mask:unsigned int result = input & mask;
And no, I didn't DV.
– Andrew Henle
Feb 25 at 13:57
0xFF
vsone_byte_bit_mask_0xFF
vsmask
, I choose the second one as the least-bad option.
– Jose
Feb 25 at 14:04
3
3
Anyway, I would use a
const
variable if your preference is the second one, to remove magic numbers and make clearer that the purpose is masking Anyone who thinks 0xFF
in the context of bit masking is a "magic number" that has to be papered over with a variable is engaging in cargo-cult programming. What possible variable can be more clear than 0xFF
as a bit mask? one_byte_bit_mask_0xFF
?– Andrew Henle
Feb 25 at 13:05
Anyway, I would use a
const
variable if your preference is the second one, to remove magic numbers and make clearer that the purpose is masking Anyone who thinks 0xFF
in the context of bit masking is a "magic number" that has to be papered over with a variable is engaging in cargo-cult programming. What possible variable can be more clear than 0xFF
as a bit mask? one_byte_bit_mask_0xFF
?– Andrew Henle
Feb 25 at 13:05
IMO, if the purpose is to mask, the word mask is clearer than any (magic) number.
– Jose
Feb 25 at 13:44
IMO, if the purpose is to mask, the word mask is clearer than any (magic) number.
– Jose
Feb 25 at 13:44
IMO, if the purpose is to mask, the word mask is clearer than any (magic) number Oh? How many bits does this mask:
unsigned int result = input & mask;
And no, I didn't DV.– Andrew Henle
Feb 25 at 13:57
IMO, if the purpose is to mask, the word mask is clearer than any (magic) number Oh? How many bits does this mask:
unsigned int result = input & mask;
And no, I didn't DV.– Andrew Henle
Feb 25 at 13:57
0xFF
vs one_byte_bit_mask_0xFF
vs mask
, I choose the second one as the least-bad option.– Jose
Feb 25 at 14:04
0xFF
vs one_byte_bit_mask_0xFF
vs mask
, I choose the second one as the least-bad option.– Jose
Feb 25 at 14:04
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54866461%2fshould-a-cast-be-used-to-truncate-a-long-variable%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
I think both will generate same binary code. As the first solution is more readable, I would use this one. You just need to be sure that uint8 is 8-but on all platforms (i guess it is)
– jaudo
Feb 25 at 12:47
Thank you @jaudo, that's also the direction I'm taking.
– Soyding Mete
Feb 25 at 12:54
11
Sidenote: Standard
stdint.h
types such asuint8_t
should be preferred instead of creating your own.– user694733
Feb 25 at 13:01