Should a cast be used to truncate a long variable?












8















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?










share|improve this question

























  • 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 as uint8_t should be preferred instead of creating your own.

    – user694733
    Feb 25 at 13:01
















8















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?










share|improve this question

























  • 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 as uint8_t should be preferred instead of creating your own.

    – user694733
    Feb 25 at 13:01














8












8








8


1






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?










share|improve this question
















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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: Standard stdint.h types such as uint8_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











  • 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 as uint8_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












4 Answers
4






active

oldest

votes


















6














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 );





share|improve this answer
























  • 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





    @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











  • @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





    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





















4














Since uint8 is unsigned, you don't have to do the masking:




6.3.1.3 Signed and unsigned integers




  1. 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.

  2. 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)

  3. 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.






share|improve this answer



















  • 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 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













  • @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



















3















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;





share|improve this answer


























  • This and your comment about using both being a WET solution is satisfying thank you @chux

    – Soyding Mete
    Feb 26 at 7:31



















-1















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).






share|improve this answer



















  • 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













  • 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 vs one_byte_bit_mask_0xFF vs mask, I choose the second one as the least-bad option.

    – Jose
    Feb 25 at 14:04











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
});


}
});














draft saved

draft discarded


















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









6














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 );





share|improve this answer
























  • 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





    @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











  • @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





    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


















6














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 );





share|improve this answer
























  • 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





    @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











  • @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





    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
















6












6








6







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 );





share|improve this answer













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 );






share|improve this answer












share|improve this answer



share|improve this answer










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 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





    @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











  • @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





    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





















  • 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





    @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











  • @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





    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



















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















4














Since uint8 is unsigned, you don't have to do the masking:




6.3.1.3 Signed and unsigned integers




  1. 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.

  2. 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)

  3. 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.






share|improve this answer



















  • 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 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













  • @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
















4














Since uint8 is unsigned, you don't have to do the masking:




6.3.1.3 Signed and unsigned integers




  1. 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.

  2. 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)

  3. 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.






share|improve this answer



















  • 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 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













  • @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














4












4








4







Since uint8 is unsigned, you don't have to do the masking:




6.3.1.3 Signed and unsigned integers




  1. 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.

  2. 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)

  3. 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.






share|improve this answer













Since uint8 is unsigned, you don't have to do the masking:




6.3.1.3 Signed and unsigned integers




  1. 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.

  2. 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)

  3. 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.







share|improve this answer












share|improve this answer



share|improve this answer










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 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













  • @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





    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











  • @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











3















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;





share|improve this answer


























  • This and your comment about using both being a WET solution is satisfying thank you @chux

    – Soyding Mete
    Feb 26 at 7:31
















3















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;





share|improve this answer


























  • This and your comment about using both being a WET solution is satisfying thank you @chux

    – Soyding Mete
    Feb 26 at 7:31














3












3








3








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;





share|improve this answer
















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;






share|improve this answer














share|improve this answer



share|improve this answer








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



















  • 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











-1















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).






share|improve this answer



















  • 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













  • 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 vs one_byte_bit_mask_0xFF vs mask, I choose the second one as the least-bad option.

    – Jose
    Feb 25 at 14:04
















-1















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).






share|improve this answer



















  • 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













  • 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 vs one_byte_bit_mask_0xFF vs mask, I choose the second one as the least-bad option.

    – Jose
    Feb 25 at 14:04














-1












-1








-1








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).






share|improve this answer














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).







share|improve this answer












share|improve this answer



share|improve this answer










answered Feb 25 at 12:58









JoseJose

1,241415




1,241415








  • 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













  • 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 vs one_byte_bit_mask_0xFF vs mask, I choose the second one as the least-bad option.

    – Jose
    Feb 25 at 14:04














  • 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













  • 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 vs one_byte_bit_mask_0xFF vs mask, 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


















draft saved

draft discarded




















































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.




draft saved


draft discarded














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





















































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







Popular posts from this blog

Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

ComboBox Display Member on multiple fields

Is it possible to collect Nectar points via Trainline?