DSPRelated.com
Forums

Datasheet Question

Started by Mauritz Jameson August 26, 2015
Section 7.2.1 on page 11 in this PDF:

http://cache.freescale.com/files/sensors/doc/app_note/AN4076.pdf

describes how to convert a 14-bit value into a decimal fraction, but I'm not sure I understand it so I'm hoping that somebody in this group could take a look at the PDF and tell me if my interpretation (see below) is correct?


1. Read in first byte (8 most significant bits) denoted MSB
2. Read in next byte (denoted LSB) where the 6 most significant bits are used and the lowest 2 bits are not used.

Which one of the following conversions (using C syntax) is correct:


double x = ((double) ((((short)(MSB << 8)) | LSB) >> 2)) / 8192.0;

OR

double x = ((double) ((((short)(MSB << 8)) | LSB) >> 2)) / 4096.0;

Mauritz Jameson <mjames2393@gmail.com> wrote:
> Section 7.2.1 on page 11 in this PDF:
> http://cache.freescale.com/files/sensors/doc/app_note/AN4076.pdf
> describes how to convert a 14-bit value into a decimal fraction, > but I'm not sure I understand it so I'm hoping that somebody in > this group could take a look at the PDF and tell me if my > interpretation (see below) is correct?
(snip)
> Which one of the following conversions (using C syntax) is correct:
> double x = ((double) ((((short)(MSB << 8)) | LSB) >> 2)) / 8192.0;
> OR
> double x = ((double) ((((short)(MSB << 8)) | LSB) >> 2)) / 4096.0;
If you want to convert to decimal, you don't want (double). Consider instead the case that you have a scaled binary number, for example 32 bits with 16 bits to the right of the binary point, that represents dollars. As a 32 bit binary integer, it represents the number of $1/65536 that you have. Consider converting it to dollars and cents. First multiply by 100, then you have the number of cents in scaled binary, with 16 bits after the binary point. (This assumes enough bits are available on the left.) Now divide by 65536 (or shift right 16 places) and you have the (truncated) number of cents. Divide by 100, the quotient is dollars, the remainder less than $1 in cents. So, the general idea of the conversion is multiplying by powers of 10, and dividing by powers of two. -- glen
, the general idea of the conversion is multiplying by
> powers of 10, and dividing by powers of two. > > -- glen
Hi Glen, And thank you for responding. I was more looking for an answer to which of the (2) proposed methods is correct? When I read the section I referred to in the PDF, it looks like it's a Q2.12 number (a decimal number scaled by 4096 which has a range between -2 and almost 2). In other words, the decimal is: -2*B[13] + sum(B[k]*(2^(k-12)),k=0,k=12) where B[13] is the 'sign' bit and B[0] is the least significant bit of the 14 bits. So, again, is the above interpretation the same as the interpretation specified in the PDF ?
On Wed, 26 Aug 2015 15:23:25 -0700, Mauritz Jameson wrote:

> Section 7.2.1 on page 11 in this PDF: > > http://cache.freescale.com/files/sensors/doc/app_note/AN4076.pdf > > describes how to convert a 14-bit value into a decimal fraction, but I'm > not sure I understand it so I'm hoping that somebody in this group could > take a look at the PDF and tell me if my interpretation (see below) is > correct? > > > 1. Read in first byte (8 most significant bits) denoted MSB 2. Read in > next byte (denoted LSB) where the 6 most significant bits are used and > the lowest 2 bits are not used. > > Which one of the following conversions (using C syntax) is correct: > > > double x = ((double) ((((short)(MSB << 8)) | LSB) >> 2)) / 8192.0; > > OR > > double x = ((double) ((((short)(MSB << 8)) | LSB) >> 2)) / 4096.0;
Oooh, many problems. I've been thinking all day so I'm just going to kinda barf out my reasoning here, and let you pick through it for the solid bits: First, if MSB is declared as an 8-bit value then with any reasonably obedient compiler (MSB << 8) = 0. You're better off with ((short)MSB << 8), but even that is fraught with problems if MSB is signed (because you can't count on 'short' being 16 bits on every processor). If it's signed -- ask, and someone will clarify. Second, you haven't said what the scaling of the data is, so just about anything can be assumed. If the number is signed and in 2's compliment, then the number ranges from -8192 to +8191. Further, if the number is meant to represent something from -1 to nearly +1, then the division by 8192 is correct. If the number is UNsigned, and the output number is supposed to range from 0 to nearly 1, then the correct divisor would be 16384, and you'll probably have troubles unless you cast to unsigned short. If I were going to write this with some hope of it being portable, and if the numbers are supposed to be signed and to range from -1.0 to almost +1.0, then I would think the following would be pretty safe: double x = ((double) ((((int16_t)MSB << 8) | LSB) >> 2)) / 8192.0; With a good ANSI-C compliant compiler and a machine that won't represent 16-bit integers it'll fail (int16_t is optional because if you're specifying it you're saying you're DEPENDING on it being 16-bit). Aside from that, if it compiles it'll probably work. I would be tempted to make the sign conversion more explicit, and avoid any exact lengths of words: double x; if ((MSB & 0x80) == 0) { x = ((double) ((((int)MSB << 8) | LSB) >> 2)) / 8192.0; } else { int number; number = -(INT_MAX + 1 - (unsigned char)MSB); number = (number << 8) | LSB; x = ((double) (number >> 2)) / 8192.0; } Even here, this only works for 2's compliment numbers (they're not guaranteed in the C standard, although I know of no processors with even a remote claim to modernity that doesn't use 2's compliment), and there's probably something else that I've missed -- whenever I start by saying "oh, this is safe for signed numbers" someone always catches something I've missed. Come to think of it, double x = ((double) ((((int)((int8_t)MSB) << 8) | (uint8_t)LSB) >> 2)) / 8192.0; may work, and be safe, and compile nicely on a lot of platforms, and has the advantage of being one "line" of code, albeit one long and cryptic line. -- www.wescottdesign.com
On 8/26/2015 8:50 PM, Mauritz Jameson wrote:
> , the general idea of the conversion is multiplying by >> powers of 10, and dividing by powers of two. >> >> -- glen > > Hi Glen, > > And thank you for responding. I was more looking for an answer to > which of the (2) proposed methods is correct? When I read the section > I referred to in the PDF, it looks like it's a Q2.12 number (a > decimal number scaled by 4096 which has a range between -2 and almost > 2). In other words, the decimal is: > > -2*B[13] + sum(B[k]*(2^(k-12)),k=0,k=12) > > where B[13] is the 'sign' bit and B[0] is the least significant bit > of the 14 bits. > > So, again, is the above interpretation the same as the interpretation > specified in the PDF ?
Unless I missed a detail, the difference between the two equations in your first post is the full scale value you expect. The second equation where you divide by 4096.0 will give you a range of &#4294967295;2.0 which is what you seem to want. Where did these two equations come from? -- Rick
Mauritz Jameson  <mjames2393@gmail.com> wrote:

>Section 7.2.1 on page 11 in this PDF: > >http://cache.freescale.com/files/sensors/doc/app_note/AN4076.pdf > >describes how to convert a 14-bit value into a decimal fraction, but I'm >not sure I understand it so I'm hoping that somebody in this group could >take a look at the PDF and tell me if my interpretation (see below) is >correct?
>1. Read in first byte (8 most significant bits) denoted MSB >2. Read in next byte (denoted LSB) where the 6 most significant bits are >used and the lowest 2 bits are not used.
>Which one of the following conversions (using C syntax) is correct:
>double x = ((double) ((((short)(MSB << 8)) | LSB) >> 2)) / 8192.0;
>OR
>double x = ((double) ((((short)(MSB << 8)) | LSB) >> 2)) / 4096.0;
Choosing between these two, the first one. Since 8192.0 = pow(2.0,13.0). This will give you a fractional value between -1 and +1 - pow(2.0,13.0). (Sticking to C-like notation in the above.) But it's not very safe. It only works if a short is 16 bits wide, and I do not believe the width of a short is in the C language standard. For some machines, a short and an int are the same width. With code like this you are assuming that "MSB << 8" places the most significant bit of the value MSB into the most significant bit of the short. On most machines/compilers this works, but not all. To make this code safe, you can use the values in "limits.h" to determine how much you really need to shift. Also, you are not converting to "decimal" in the above, but you are converting to double, which can be converted to a decimal string by library functions such as sprintf(). Steve
Tim Wescott <tim@seemywebsite.com> wrote:
> On Wed, 26 Aug 2015 15:23:25 -0700, Mauritz Jameson wrote:
>> Section 7.2.1 on page 11 in this PDF:
>> http://cache.freescale.com/files/sensors/doc/app_note/AN4076.pdf
>> describes how to convert a 14-bit value into a decimal fraction, but I'm >> not sure I understand it so I'm hoping that somebody in this group could >> take a look at the PDF and tell me if my interpretation (see below) is >> correct?
(snip)
>> double x = ((double) ((((short)(MSB << 8)) | LSB) >> 2)) / 8192.0;
(snip)
> First, if MSB is declared as an 8-bit value then with any reasonably > obedient compiler (MSB << 8) = 0. You're better off with > ((short)MSB << 8), but even that is fraught with problems if MSB is > signed (because you can't count on 'short' being 16 bits on every > processor). If it's signed -- ask, and someone will clarify.
C promotes anything smaller than int first to int before doing just about anything else, such as shift.
> Second, you haven't said what the scaling of the data is, so just about > anything can be assumed. If the number is signed and in 2's compliment, > then the number ranges from -8192 to +8191. Further, if the number is > meant to represent something from -1 to nearly +1, then the division by > 8192 is correct.
But why the conversion do (double)? The OP wants to convert to decimal, at least that is what it says. Now, someone could use the new decimal forms in IEEE 754-2008.
> If the number is UNsigned, and the output number is supposed to range > from 0 to nearly 1, then the correct divisor would be 16384, and you'll > probably have troubles unless you cast to unsigned short.
> If I were going to write this with some hope of it being portable, and if > the numbers are supposed to be signed and to range from -1.0 to almost > +1.0, then I would think the following would be pretty safe:
> double x = ((double) ((((int16_t)MSB << 8) | LSB) >> 2)) / 8192.0;
> With a good ANSI-C compliant compiler and a machine that won't represent > 16-bit integers it'll fail (int16_t is optional because if you're > specifying it you're saying you're DEPENDING on it being 16-bit). Aside > from that, if it compiles it'll probably work.
> I would be tempted to make the sign conversion more explicit, and avoid > any exact lengths of words:
(snip)
> Even here, this only works for 2's compliment numbers (they're not > guaranteed in the C standard, although I know of no processors with even > a remote claim to modernity that doesn't use 2's compliment), and there's > probably something else that I've missed -- whenever I start by saying > "oh, this is safe for signed numbers" someone always catches something > I've missed.
Unisys is still producing ones complement machines. They have some relation to some older machines, but are actually new.
> Come to think of it,
> double x = ((double) ((((int)((int8_t)MSB) << 8) | > (uint8_t)LSB) >> 2)) / 8192.0;
> may work, and be safe, and compile nicely on a lot of platforms, and has > the advantage of being one "line" of code, albeit one long and cryptic > line.
-- glen
I'm sorry for not explaining this properly.

I am trying to understand the conversion scheme described *in the PDF* and I'm not sure that the conversion I've proposed (i.e. the C expression) is correct?? So if you could take a look at section 7.2.1 in the PDF and tell me if the two bytes together are supposed to represent a Q2.12 number (-2.0 to 2.0) or a Q1.13 number (-1.0 to 1.0) that would be helpful. 

> Where did these two equations come from? >
The two equations come from me. They're an expression of how I think the conversion scheme described in the PDF (section 7.2.1) is supposed to be interpreted. Either the 2 bytes represent a Q2.12 number of they represent a Q1.13 number. I'm leaning towards Q2.12 but I'm not sure..
Mauritz Jameson <mjames2393@gmail.com> writes:

>> Where did these two equations come from? >> > > > The two equations come from me. They're an expression of how I think > the conversion scheme described in the PDF (section 7.2.1) is supposed > to be interpreted. Either the 2 bytes represent a Q2.12 number of they > represent a Q1.13 number. I'm leaning towards Q2.12 but I'm not sure..
It's a Q2.12 (note I would denote this as an A(1, 12)). The maximum positive scaled (unscaled) value is 2 - 1/4096 (8191), and the minimum negative scaled (unscaled) value is -2 (-8192). To get the scaled value x from the unscaled value X, divide by 2^12 = 4096: x = X / 4096. -- Randy Yates Digital Signal Labs http://www.digitalsignallabs.com