Signed number grief.

Rethinking my earlier post about dealing with a left-justified, less-than-16-bit signed integer: you want a general method of managing that value. Specifically, you want something that doesn't rely on the size of an integer for any particular platform. Not a bad idea, if you want to be able to port an application to the Arduino Due, with its 32-bit integers.

If you want that particular generality - if that's not an oxymoron - I don't see a way to avoid examining the sign bit, and then doing something to the data. You can do that as described previously in this post:

tmd3:

I'll note that you can avoid the 6-bit shift by folding the shift operation into the floating-point multiplication, like this:

const long deviceBits = 10;
const long deviceWordLength = 16;
const long deviceMask = -(1 << (deviceWordLength - 1));
const float deviceScaleFactor = 0.25;
int x;
...
    if (x & deviceMask) {
      x |= deviceMask;
    }
    someFloatVariable = x * (deviceScaleFactor * (1.0/(1 << (deviceWordLength-deviceBits))));

It has the same advantages as the version in the post referenced above. I think - but don't know - that it will eliminate the 6-bit shift from the compiled code, because the compiler will recognize the factor in the last statement as being composed entirely of constants, and will do that calculation at compile time. It adds a characteristic of the sensor to the code - 0.25 degrees C per tick - but that was already embedded in the program, and it might as well be at the top where it's easy to find and modify. It also adds the characteristic that the device output is a 16-bit word.

I've tested that code with the characteristics of the device described, and I think it works. I haven't tested it with other characteristics to verify its general-ness.

Using the stimmer-fungus technique described above, the general code might look like this:

const long deviceBits = 10;
const long deviceWordLength = 16;
const long deviceMask = (1 << deviceBits) - 1;
const float deviceScaleFactor = 0.25;
int x;
...
    x >>= (deviceWordLength-deviceBits);
    x += 1 << (deviceBits - 1);
    x &= deviceMask;
    x -= 1 << (deviceBits - 1); 
    someFloatVariable = x * deviceScaleFactor;

That code works in the specific case, too. It's not checked for generality.

For more complete generality, you could add a const someType deviceOffset, to manage gizmos whose output values aren't centered on zero. That might be the input code that corresponds to a zero reading, in which case the constant would be int and added to x, or it might be the reading that a zero input describes, in which case the constant would be float and added to someFloatVariable. Then, with just a couple of value changes to constants, you could get your readings in, say, Kelvin, or - oh joy of joys - Farenheit or Rankine; or, you could get your readings from an analog conversion on something like a 4-20mA temperature transducer, or a single-supply LM35 circuit.

You want to generalise it even more?

typedef long device_t;

const device_t deviceBits = 10;
const device_t deviceWordLength = sizeof(device_t)<<2;
const device_t deviceMask = (1 << deviceBits) - 1;
const float deviceScaleFactor = 0.25;

I don't see how this helps:

typedef long device_t;
...
const device_t deviceWordLength = sizeof(device_t)<<2;

deviceWordLength is a characteristic of the input device. You can call it the length of the device's reply, or you can call it the position of the reply's sign bit. In this code, it depends only on the characteristics of the long data type.

What am I missing?

Oh great. A slow imprecise floating point divide to replace a fast precise bit shift.

You're just trying to turn a 10-bit value into 16 bits to convert to float.

How about, insane as this might seem, load up the 10 bits into a 16-bit int and extend (copy) the sign in bit 9 from bit 10 to bit 15 then convert to float.

Here is my proposal:

(signed short) ((x & 0x0200)?(x|0xfc00):x)

Essentially, left extending the sign.

Here is a generic solution; it is standard conforming as long as T is some variant of int ( signed, short, long...).

template <typename T, unsigned B>
inline T sign_extend(const T x)
{
  struct {T x:B;} s;
  return s.x = x;
}

So for 10-bits extended to 16-bits:

int result = sign_extend< signed int ,10 >(x);

Noting that -1<<10 will generate the correct bit mask to sign-extend the ten-bit quantity to whatever word size… here you can see Bitlash doing the calculation on OS X with a 64 bit word size:

$ bitlash
bitlash here! v2.0 (c) 2012 Bill Roy -type HELP- 1000 bytes free
> print -1<<10:x
FFFFFFFFFFFFFC00

So, dhenry's proposal can be generalized to:

(signed short) ((x & 0x0200)?(x|(-1<<10)):x)

-br

tmd3:
I don't see how this helps:

typedef long device_t;

...
const device_t deviceWordLength = sizeof(device_t)<<2;



**deviceWordLength** is a characteristic of the input device. You can call it the length of the device's reply, or you can call it the position of the reply's sign bit. In this code, it depends only on the characteristics of the *long* data type.

What am I missing?

Sorry, I missed what that bit was doing.

Ignore me, I know nothing :stuck_out_tongue:

A little test code.

> ls
function extend10 {if arg(1)&0x200 return arg(1)|(-1<<10); else return arg(1); };
function px10 {print extend10(arg(1)):x;};
> px10(0x200)
FFFFFFFFFFFFFE00
> px10(0x1ff)
1FF
> px10(0x10)
10

-br

GoForSmoke:
A slow imprecise floating point divide to replace a fast precise bit shift.

I think the poster refers to this line:

    someFloatVariable = x * (deviceScaleFactor * (1.0/(1 << (deviceWordLength-deviceBits))));

Looking at these concerns one by one, in reverse order:

  • floating point divide: Indeed, there's a floating point division explicitly coded in that line. But, the arguments are all constants, along with everything else inside the parentheses. Based on the explorations of compiler's output for other complicated floating point expressions, I believe that the compiler will detect that the expression evaluates to a constant, optimize the internal calculations out of existence, and wind up with a single floating point constant. I don't see that there will be a division in the executed code.
  • imprecise: I don't think so. The operation that's being replaced is a bit shift right, equivalent to division by 2N, for an integer N. The floating point representation of 2-N is a sign bit, an exponent, an implied one, and 24 zeroes. Division will result in a change of exponent only, and won't result in any loss of precision. It's true that, in general, precision is lost in a floating point division, but not in this case, and that's a not consequence of the particular parameters of this calculation, but rather of the very operation that's being performed.
  • slow: Yes, a floating point division is slow. But, because everything involved is a constant, it happens at compile time, rather than at runtime. The Arduino won't execute a floating point division based on this code.

If you're skeptical about the compiler optimizing away the division, you can force it like this:

...
const float deviceScaleFactorComplete = deviceScaleFactor * (1.0/(1 << (deviceWordLength-deviceBits)));
...
    someFloatVariable = x * deviceScaleFactorComplete;

... just trying to turn a 10-bit value into 16 ...

Yes. But, the OP expressed concern about the fact that some of the proposed techniques rely on the fact that an integer has a 16-bit representation - an implementation-dependent size - and wanted to know how to make his code more general. That concern is of more than merely academic interest, because "on the Arduino Due, an int stores a 32-bit (4-byte) value." See it here: http://arduino.cc/en/Reference/Int. It's not at all unlikely that some of us will be porting code we write today to that platform, so there's certainly value in coding for the general case.

... load up the 10 bits into a 16-bit int and extend (copy) the sign in bit 9 from bit 10 to bit 15 ...

It's even easier than that. The device provides a 10-bit signed number, left-justified in a 16-bit word. For a 16-bit platform, it's as simple as loading the value into a signed int, and shifting it six bits to the right; the sign bit is automatically extended. But that's not what the OP asked for: he asked for help in writing code that could easily accommodate a different number of significant bits from the input device, and he expressed concern about algorithms that relied on a 16-bit integer size. Because the issue has practical implications, it's getting a bit of attention. It's certainly made me wonder how deeply I've embedded implementation-dependent parameters into some of my own favorite code.

I put this to some exercise:

#define FLP(pin)  {PORTD |= 1<<1; PORTD &=~1<<1;}

void setup(void) {
  PORTD &=~(1<<1);
  DDRD |= (1<<1);
}

const long deviceBits = 10;
const long deviceWordLength = 16;
const long deviceMask = -(1l << (deviceWordLength - 1));
const float deviceScaleFactor = 0.25;
int x=511;
float someFloatVariable;

void loop(void) {

  FLP(OUT_PIN); FLP(OUT_PIN);  FLP(OUT_PIN); //flip out pin
  if (x & deviceMask) {
    x |= deviceMask;
  }
  someFloatVariable = x * (deviceScaleFactor * (1.0/(1 << (deviceWordLength-deviceBits))));
  FLP(OUT_PIN); FLP(OUT_PIN); //flip out pin
  someFloatVariable = (signed short) ((x & 0x0200)?(x|(-1<<10)):x);
  FLP(OUT_PIN);
  delay(10);
}

Question: how much time does the floating point approach take than the integer shift approach?
1: less time;
2: the same;
3: 8x;
4: all the above.

tmd3:

... load up the 10 bits into a 16-bit int and extend (copy) the sign in bit 9 from bit 10 to bit 15 ...

It's even easier than that. The device provides a 10-bit signed number, left-justified in a 16-bit word. For a 16-bit platform, it's as simple as loading the value into a signed int, and shifting it six bits to the right; the sign bit is automatically extended. But that's not what the OP asked for: he asked for help in writing code that could easily accommodate a different number of significant bits from the input device, and he expressed concern about algorithms that relied on a 16-bit integer size. Because the issue has practical implications, it's getting a bit of attention. It's certainly made me wonder how deeply I've embedded implementation-dependent parameters into some of my own favorite code.

For a 16-bit platform, it's as simple as loading the value into a signed int ** with the top bit as bit 15, maybe load it direct, shift 6 bits left after determining 6 bits for 16 bit int or some other way but there has to be that trick step or the next trick won't work **, and shifting it six bits to the right; the sign bit is automatically extended.

When you put in all the steps you get the true length of the code. Then the other simple ways don't seem so bad.

The best routine would run completely on CPU registers in less a microsecond, but that wouldn't be 'generic'.

For a 16-bit platform,

Try it and you would be surprised.

Why? Did 16-bit platforms change? I wrote a lot of code on 8080's and 8088's and never had trouble with bits then. Come to think, I did a lot of 16 bit word code on 6502's (8 bit CPU, 16 bit languages) as well. No trouble with bits, they have bit rotate as well as bit shift commands.

When I use 16-bit ints on Arduino that uses 16-bit addressing, what is the platform? Isn't that set by address length?

Note that I quoted someone else in that post and added a step I saw left out. But yah that RTC doesn't give a straight 10 bits, does it? The bits have to practically be mapped to the target. Good news is 32-bits only needs either 0x0000 or 0xFFFF added to a 16-bit solution.

Having reflected a bit more on this, I see that in this code:

const long deviceBits = 10;
const long deviceWordLength = 16;
const long deviceMask = (1 << deviceBits) - 1;

there's no need for any of the constants to be declared long. The counting constants, describing length of the significant data and length of the word it resides in, can obviously be byte-sized, at least until we start using 257-bit integers. The mask doesn't need to be any bigger than the input data word, in this case int.

Hmmph.

Hmmph.

How about this piece:

(1 << deviceBits)

:slight_smile:

That's not the mask I wanted. I wanted this one:const long deviceMask = -(1 << (deviceWordLength - 1));The mask isolates the sign bit for testing, and extends the sign bit when the sign bit is set. It needs to be as long as the type of the input, which is int in this case, but it doesn't need to be longer.

It should be int, not long, then.

That's not the mask I wanted.

I think you will find that a generic approach is not as optimal as having a series of specific approaches (one for 10-bits, one for 12-bits, etc.) that you can link in for your specific applications.

If those numbers came from an adc module, many allow left-aligned adcs making the shift approach a lot more appealing.