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 mantissa : 11;
int16_t exponent : 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.mantissa * (double)(1 << t.linear.exponent);
}
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_tvariable, 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_doublefunction would then just take in uint16_t values.
Further Reading
- PMBus Specification – Part II
- Bit Twiddling Hacks
- Sign Extending From a Constant Bit-Width
- Excel Add-In Functions for PMBus Number Formats | Analog Devices – can be useful for checking your conversion routines.

Beware that this will fail for negative exponents since the result of a negative shift quantity is undefined. In practice it will results in a very large number due to wraparound on many platforms. I would recommend using ldexp instead.
Oh, and be careful with type-punning via unions since it is undefined behavior and overeager optimizers (GCC, clang, etc) may therefore silently assume that the path is impossible and generate unexpected code. The usual workaround is to use memcpy instead.
This might help check your answers:
https://www.maximintegrated.com/en/design/technical-documents/tutorials/6/6943.html
Amazing a good deal of beneficial information
I’d avoid bitfields for almost all purposes (including this). The problem is that you don’t know what they do. No, you don’t. If you think you do, read the Standard. The Standard says you don’t know what they do!
“Hack”. Also, the main bitfield problem is portability, and that definitely can cause problems. But for an internal representation that isn’t shared across systems or dependent on an external endianness, I have a hard time seeing the true trouble. Happy to hear you describe a breakdown scenario.
What you’re calling the base is actually the mantissa and what you are calling the mantissa is actually the exponent.