The LINEAR11 data format is used by PMBus (Power Management Bus) devices. LINEAR11 is a 16-bit number which is composed of a pair of two's complement signed integers. When I was faced with converting from LINEAR11 to floating-point, I ended up running into sign extension problems.

Luckily, I stumbled upon a simple trick that we can use to convert from LINEAR11 to floating-point.

**Table of Contents:**

# About LINEAR11

The PMBus Specification defines the LINEAR11 data format as a 16-bit number that consists of an 11-bit mantissa and 5-bit exponent. Due to these mantissa and exponent sizes, LINEAR11 offers a range 1024 steps and powers-of-2 resolution. The LINEAR11 format is typically used to represent non-output voltages and temperatures in a PMBus system.

The conversion function is defined as follows:

`V = X * 2^N`

Where:

- V is the decimal value corresponding to the LINEAR11 data
- X is a signed 11-bit 2's complement integer
- N is a signed 5-bit 2's compliment integer

## A Simple Conversion Trick

When I first worked at converting a LINEAR11 number to floating-point, I ran into trouble with parsing the bitfields and getting the numbers to properly sign extend.

Whenever I'm troubled by something in the bitfield arena, I check the Standford Graphics Lab Bit Twiddling Hacks page. I was pleased to see Sign extending from a constant bit-width as one of the hacks.

To quote Bit Twiddling Hacks:

Sign extension is automatic for built-in types, such as chars and ints. But suppose you have a signed two's complement number, x, that is stored using only b bits. Moreover, suppose you want to convert x to an int, which has more than b bits. A simple copy will work if x is positive, but if negative, the sign must be extended. For example, if we have only 4 bits to store a number, then -3 is represented as 1101 in binary. If we have 8 bits, then -3 is 11111101. The most-significant bit of the 4-bit representation is replicated sinistrally to fill in the destination when we convert to a representation with more bits; this is sign extending. In C, sign extension from a constant bit-width is trivial, since bit fields may be specified in structs or unions.

Using that for inspiration, I created a `linear11_t`

type:

```
typedef struct
{
int16_t base : 11;
int16_t mantissa : 5;
} linear11_t;
```

We also define a `linear11_val_t`

union type that can be used while reading/writing in the LINEAR11 format:

```
typedef union
{
linear11_t linear;
uint16_t raw;
} linear11_val_t;
```

Using the `linear11_t`

structure, we can write straightforward conversion functions:

```
inline double linear11_to_double(linear11_val_t t)
{
return t.linear.base * (double)(1 << t.linear.mantissa);
}
```

Now we can look at this conversion function in context. When we want to read a LINEAR11 value, we declare an `linear11_val_t`

variable. We can then use the raw `union`

member to interface with our device-facing function that provides a `uint16_t`

output value. After we populate our `linear11_val_t`

variable, we can use it as input into the conversion function.

```
double regulator_temperature_internal(void)
{
linear11_val_t fixed_temp;
double temp = 0.0;
int r = get_internal_temperature(&fixed_temp.raw);
if(r >= 0)
{
temp = linear11_to_double(fixed_temp);
}
return temp;
}
double regulator_temperature_external(void)
{
linear11_val_t fixed_temp;
double temp = 0.0;
int r = get_external_temperature(&fixed_temp.raw);
if(r >= 0)
{
temp = linear11_to_double(fixed_temp);
}
return temp;
}
```

Easy, wasn't it?

If you prefer, you can eliminate the need for `linear11_val_t`

by having the structure hidden within the conversion function. Your `linear11_to_double`

function would then just take in `uint16_t`

values.