Normal numbers in, weird numbers out

OK, I'm stumped. I'm also up way past my bedtime.

The following code is supposed to calculate the average of a triplet of readings which has been previously summed, then display them in the form "2.3,-4.9, 3.3". For some reason this step seems to not be working (I've already verified that the summation is happening correctly).

Integers are 32-bit. _count is incremented each time a triplet is summed into _readings (not shown here). _count is expected to be in the 50-500 range.

_scale = 20000.

Positive numbers seem to be getting calculated correctly, but negative numbers are getting turned into numbers around 400. Given the nature of the beast, controlling the numbers going in isn't really in the cards (unless I get desperate).

The generated machine code (ARM) looks absolutely wacky to me -- I suspect clever optimizations, but I'm hoping they're not TOO clever.

I've been over this about a bazzilion times looking for overflows, and can't see any opportunities.

Help?

{ int readBuf[3];

for (size_t n = 0; n < 3; ++n) { readBuf[n] = (_scale * ((_readings[n] + _count / 2) / _count) +

32768) / 65536; _readings[n] = 0; }

_count = 0;

sprintf(_scratchText, "%2d.%d,%2d.%d,%2d.%d", readBuf[0] / 10, readBuf[0] % 10, readBuf[1] / 10, readBuf[1] % 10, readBuf[2] / 10, readBuf[2] % 10);

return _scratchText; }

--

Tim Wescott 
Wescott Design Services 
http://www.wescottdesign.com
Reply to
Tim Wescott
Loading thread data ...
:

are you sure that int is a 32bit var?

_scale, _readings, _count are signed integers?

By the way why you use the _ prefix to some variables? Because they are glo bals?

Bye Jack

Reply to
Jack

I forgot: if it's C++ it should be ok, but if it's C, declaring a var inside the for( ) may not work as expected. Also size_t what kind of variables is?

lobals?

Reply to
Jack

It's been allowed in C for over 15 years.

Reply to
Philip Lantz

not in the compilers I'm using...

Bye Jack

Reply to
Jack

Could you give a complete code snippet and some sample data so others here can try to reproduce the problem?

Reply to
Paul Rubin

Can you give an example of the /exact/ value of _readings[n] and readBuf[n] in one case that is wrong? ("Around 400" isn't very helpful.)

And give the output you expect for a known value of _readings[n]--I don't think you're simply trying to print _readings[n]/count with appropriate rounding; there's some range conversion going on here, too, right?

What is the type of _readings, _count, and _scale?

I assume that "+ _count / 2" and "+ 32768" are for rounding. These aren't going to work right for negative numbers, and you're going to have to fix them. But I don't think they're causing the problem you're stumped on-- they'll cause an off by 1 error, I think. (Past my bedtime, too.)

Philip

Reply to
Philip Lantz

Can you give us a /complete/ example - with the full definitions of the other variables? In making such compilable and testable examples, it is not uncommon for the problem to become obvious - and it certainly gives others a better chance of spotting the issue.

And it can give to give some compiler details (target, version, any special flags, language and standard).

So far, my only guess is that _count is declared as an unsigned int, and _readings[n] is a signed int, causing _readings[n] to be converted to an unsigned int (the conversion will just be a re-interpretation on the ARM). But that means your _readings[n] value of -1 suddenly becomes

4294967295.

Try adding "-Wconversion" to your list of warning flags (and "-Wsign-conversion" for C++).

Reply to
David Brown

It is allowed in C99 onwards. But if you are stuck on C90, it is not allowed.

However, it will either work fine, or be a compile-time error - it would take a thoroughly broken compiler for it to "not work as expected".

Reply to
David Brown

This is not a bug, but why are you using size_t here? The most obvious choice is "int" (I don't use plain "int" very often in my own code, but it would be fine here). Alternatives would be things like uint32_t (or uint_fast8_t if you like maximal portability and don't mind the ugliness). For C++, "auto" would be the norm these days.

"size_t", on the other hand, is used for measuring the size of memory blocks that may be larger than can be expressed in an "int" or "unsigned int".

Reply to
David Brown

(snip)

I haven't figured out the rest, but this looks strange.

If readBuf[i] is negative, so should readBuf[i]/10 and readBuf[i]%10.

I believe C used to allow for two ways for negative division, but now only one.

The example you show doesn't have sign on both parts.

-- glen

Reply to
glen herrmannsfeldt

It's because you might be on a machine with 64 bit addresses and 32 bit ints. So you use size_t as subscripts because you might need indexes that won't fit in an int.

Reply to
Paul Rubin

Yes, very important if the index runs from 0 to < 3. ;-) What does an index have to do with the size of an address? It only has to do with the size of the indexed vector.

--
Reinhardt
Reply to
Reinhardt Behm

Yes. The ARM Cortex is all 32-bit, and the assembly code is using entire registers.

Class member variables.

--
www.wescottdesign.com
Reply to
Tim Wescott

Well, yes, except then I only count up to three. In this case that's not going to change, unless the number of free dimensions in the universe suddenly changes. If _that_ happens I think I'll have bigger problems than just needing different gyros, and different code to accommodate them.

--
www.wescottdesign.com
Reply to
Tim Wescott

It's a habit -- if it goes into a pair of square braces, I make it a size_t.

What's this "auto" thing? Can you just tell the compiler to pick the best size for a given situation, then?

--
www.wescottdesign.com
Reply to
Tim Wescott

I'll need to check, but yes, _count is probably unsigned.

Does the latest gnue compiler include -Wconversion and -Wsign-conversion in -Wall, or is that a -Wextra thing?

--
www.wescottdesign.com
Reply to
Tim Wescott

Thank you -- the _count type was, indeed, unsigned. I didn't really need it to be, so I just made it an int, and all is well.

Someone mentioned that my decimal conversion would also suffer, so I changed the print around. Here's the current code (which still has rounding issues -- I'm going to close my eyes to that for the moment: this is hobby stuff and I should be doing work-work!).

I appreciate the effort of all of you -- this code is in a very difficult to debug spot, because it's being used to communicate with a box that has to be powered up at the same time as the processor and then kept happy by it. So as soon as I hook the debugger up my communications link crashes

-- and this code is driven by the communications link. When I run into issues it's all cut and try or close code inspection, and I was running into a blind spot with the "unsigned _count" thing.

unsigned pos = 0;

for (size_t n = 0; ;) { int readBuf = (_scale * ((_readings[n] + _count / 2) / _count) +

32768) / 65536;

if (readBuf < 0) { pos += sprintf(_scratchText + pos, "-%d.%d", (-readBuf) / 10, (- readBuf) % 10); } else { pos += sprintf(_scratchText + pos, "%2d.%d", readBuf / 10, readBuf % 10); } _readings[n] = 0;

if (++n >= 3) { break; }

_scratchText[pos++] = ','; }

_count = 0;

return _scratchText;

--

Tim Wescott 
Wescott Design Services 
http://www.wescottdesign.com
Reply to
Tim Wescott

No, nothing that smart; it just uses the type of the initializer.

Reply to
Philip Lantz

If _count is unsigned, then _readings[n] + _count /2 will convert _readings[n] to unsigned, which will give unexpected values for negative numbers.

Reply to
Richard Damon

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.