1

I am struggling with the realization of a signed long long int variable having the value 1 set in its integer part. Looking at the 16.16 implementation with a signed long int variable it does work like this:

static signed long int varInit = 1 << 16; /* alternatively: 65536 */
unsigned short int integer_part,fractional_part;
fractional_part = ((unsigned short int *) &varInit)[0];
integer_part = ((unsigned short int *) &varInit)[1];

This leads to the following representation: 0001.0000

Unfortunately I cannot get it to work in the 32.32 representation:

static signed long long int varInit = 1 << 32;
unsigned long int integer_part,fractional_part;
fractional_part = ((unsigned long int *) &varInit)[0];
integer_part = ((unsigned long int *) &varInit)[1];

I get the following gcc warning: warning: left shift count >= width of type

What am I getting wrong?

I am trying to get the following representation: 00000001.00000000

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
kvirk
  • 97
  • 1
  • 10
  • Does this answer your question? [left shift error](https://stackoverflow.com/questions/13937598/left-shift-error) – Shawn Aug 22 '22 at 18:31
  • 3
    Note that `((unsigned short int *) &varInit)[0]` breaks strict aliasing, and thus will lead to undefined behavior. – Some programmer dude Aug 22 '22 at 18:33
  • 4
    `1 << 32` is undefined if `int` is 32 bits. Please use `1LL << 32`. – Weather Vane Aug 22 '22 at 18:41
  • `((unsigned short int *) &varInit)[0]` that is invalid. Just use `varInit >> 16` if you mean that. `What am I getting wrong?` https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule and just use union. – KamilCuk Aug 22 '22 at 18:55
  • 1
    I can think of three ways of extracting the integer and fractional parts of a fixed-point representation: (1) using `/` and `%`, (2) using shifting and masking, and (3) using pointers. Method 1 has the advantage that it works for any fixed-point precision, not just powers of two. Method 2 has the advantage that might be more efficient. Method 3 is probably also efficient, but it has a couple of significant disadvantages: it's not as obvious how it works, it likely suffers from byte order issues, and it can suffer from other issues as well, like strict aliasing. So I don't recommend #3. – Steve Summit Aug 22 '22 at 19:32
  • (With that said, though, your problem wasn't because you used method 3, but because of the way you set your initial value up, using shifting, and the problem you had there can pretty easily befall method 2 as well.) – Steve Summit Aug 22 '22 at 19:32
  • Thanks a lot for all of your helpful comments. I do really appreciate that my question has been answered honestly, with that much input, the good old way. – kvirk Aug 22 '22 at 20:11
  • Side question: in your 16.16 representation, you noted that 65536 corresponded to `0001.0000`. What would 65537 correspond to? `0001.0001`, or 1.0000152587890625? That is, how do you intend to use `integer_part` and `fractional_part` to form an actual fraction? – Steve Summit Aug 22 '22 at 20:29
  • @SteveSummit: Right, 0001.0001 is the representation I am using for 65537. The printed result will actually have base 16 (e.g.: 0004.F3E3). – kvirk Aug 23 '22 at 13:06

2 Answers2

4

This expression

1 << 32

has undefined behavior because the operands have the type int and the result also has the type int.

From the C Standard (6.5.7 Bitwise shift operators)

3 The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined

At least use the expression

1LL << 32
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Thanks a lot, I wasn't aware of this, I should take a look at the current C Standard . IN the annotated ANSI C standard book that case isn't explicitly pointed out (Section 6.3.7 Bitwise shift operators). – kvirk Aug 22 '22 at 20:09
1

I am trying to get the following representation: 00000001.00000000 (hexadecimal).

Do not use int, long, long long as their widths vary from platform to platform. Use (u)int32_t, (u)int64_t for consistency.

1 << 32 is int math - often too narrow for shifting 32 bits. To form the binary value 1_00000000_00000000_00000000_00000000, use (u)int64_t math.

Do not assume byte layout of wide integer types with code like fractional_part = ((unsigned short int *) &varInit)[0];. Use math and let the compiler emit optimal code.


#include <stdint.h>

static int64_t varInit = ((int64_t)1) << 32;
// or 
static int64_t varInit = INT64_C(1) << 32;

int32_t integer_part = varInit >> 32;
uint32_t fractional_part = varInit & 0xFFFFFFFFu;
// or 
int32_t integer_part = varInit / 0x100000000;
uint32_t fractional_part = varInit % 0x100000000u;
 
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256