Dynamically resizing a buffer

F

Flash Gordon

cr88192 wrote, On 22/08/07 13:20:
doubling is probably too steep IMO.

I usually use 50% each time ('size2=size+(size>>1);').
25% may also be sane ('size2=size+(size>>2);').

When you want to divide, divide. It is far easier for people to read and
shows your intention. It is over 15 years since I saw a compiler that
would not optimise division by a power of 2 to a shift.
33% may also be good.

33% is ugly though:
size2=size+(size>>2)+(size>>4)+(size>>6); //bulky
size2=size+size*33/100; //critical buffer size limit

Or more accurately and clearly
size2 = size + size/3;

Or, if you can put the result back in size
size += size/3;
but is a nice 4/3 ratio...

Nice is whatever gives a decent performance.
 
C

CBFalconer

Philip said:
Richard wrote:
.... snip ...


I've already stated that the final amount of space required isn't
known a priori. It could be thousands of bytes, it could be
millions. A linearly-growing buffer is not appropriate for this
situation, which is why I chose an exponentially-growing buffer.

And the choice of strategy depends on the usage. That is why my
ggets uses linear allocation (normal use is interactive input -
limited size) while my hashlib uses doubling (possibly millions of
entries).
 
C

cr88192

Flash Gordon said:
cr88192 wrote, On 22/08/07 13:20:

When you want to divide, divide. It is far easier for people to read and
shows your intention. It is over 15 years since I saw a compiler that
would not optimise division by a power of 2 to a shift.

shifts are obvious enough...

Or more accurately and clearly
size2 = size + size/3;

odd that I missed such an obvious option...
makes me look stupid, oh well...

Or, if you can put the result back in size
size += size/3;


Nice is whatever gives a decent performance.

maybe, or whatever best follows a natural growth curve, or something...

 
¬

¬a\\/b

At one point I even had a /* your choice */ comment on
the exit() call, but decided it was too distracting and
removed it for fear it would provoke responses on side-
issues ... Damned if I do, damned if I don't, I guess.

and if it would be, why have you to fear?
 
F

Flash Gordon

cr88192 wrote, On 23/08/07 00:03:
shifts are obvious enough...

It is not as obvious. I can read shifts, but for something like the
above I then have to think to know what the scaling factor is, but with
division the scaling factor is actually stated. I also don't have to
worry about precedence because for arithmetic C just follows the rules I
was taught before I saw my first computer. I also don't have to worry
about whether it is a signed number (shifting a negative number is not
required to act as division). Is is also easier to change the factor
with division.

Finally, and most importantly, a division expresses the intent, shift
expresses a way of achieving that intent. It is always better to express
your intent where possible until you have *proved* you need to worry
about the details.
odd that I missed such an obvious option...
makes me look stupid, oh well...

You missed it because you insist on looking for micro-optimisations
instead of trying to express your intent clearly. Oh wait, that is
another way of saying it makes you look stupid. This is because this
sort of micro-optimisation has been stupid for many years because the
compiler will do it for you so wasting your time trying to beet the
compiler is stupid.
maybe, or whatever best follows a natural growth curve, or something...

I can't think of a good reason for wanting less than decent performance.
Of course, I consider all resources when thinking about performance, so
tell me what I've missed.
 
J

jaysome

Hello clc,

I have a buffer in a program which I write to. The buffer has
write-only, unsigned-char-at-a-time access, and the amount of space
required isn't known a priori. Therefore I want the buffer to
dynamically grow using realloc().

A comment by Richard Heathfield in a thread here suggested that a good
algorithm for this is to use realloc() to double the size of the buffer,
but if realloc() fails request smaller size increments until realloc()
succeeds or until realloc() has failed to increase the buffer by even
one byte.

The basic idea is below. The key function is MyBuffer_writebyte(), which
expects the incoming MyBuffer object to be in a consistent state.

Are there any improvements I could make to this code? To me it feels
clumsy, especially with the break in the 3-line while loop.

struct mybuffer_t {
unsigned char *data;
size_t size; /* size of buffer allocated */
size_t index; /* index of first unwritten member of data */
};

typedef struct mybuffer_t MyBuffer;

void MyBuffer_writebyte(MyBuffer *buf, unsigned char byte) {
if(buf->size == buf->index) {
/* need to allocate more space */
size_t inc = buf->size;
unsigned char *tmp;
while(inc>0) {
tmp = realloc(buf->data, buf->size + inc);
if (tmp!=NULL) break; /* break to preserve the size of inc*/
inc/=2;
}
if(tmp==NULL) {
/* couldn't allocate any more space, print error and exit */
exit(EXIT_FAILURE);

Nobody mentioned this so far, but I think it's worth mentioning.

Your immediate above comment is wrong. The exit() function does not
necessarily print an error. In this case, the exit() function
terminates the program and, according to the C standard, allowably
silently.

In fact, most implementations I've come across don't print anything
out as a result of calling the exit() function--the program simply
terminates silently, and the user is left waiving his or her hands in
the air.

And even if exit() did print out an error, what would you expect for
it to print out in this case? Surely it can't print out the following
(unless you expect C to have a crystal ball that intelligently reads
comments):

couldn't allocate any more space, print error and exit

If you want to print an error and then "exit()", you'll have to print
the error out on your own. Something like this would work:

if ( !tmp )
{
/* couldn't allocate any more space, print error and exit */
fprintf(stderr, "couldn't allocate any more space\n");
exit(EXIT_FAILURE);
}

If you do something like the above, make sure you include <stdio.h>
for the prototype for fprintf() and <stdlib.h> for the prototype for
exit() and the definition of the macro EXIT_FAILURE.

One of the problems with outputting an error message to stderr (or
stdout) and then calling exit() is that your user may never see the
error message. There is nothing in the C standard that prevents an
operating system, or more appropriately, a run time environment, from
terminating your program when exit() is called without you having a
glimmer of a chance of viewing the message output to stderr (or
stdout).

When you decide to call the function exit(), you are basically, as a
programmer, throwing your hands up in the air (and most likely to have
the user emulate you) and claiming that "this condition should never
happen". After all, the exit() function simply terminates the program
as far as the user is concerned.

Given this, perhaps you should consider some alternatives to using or
not using exit(). Far better as an alternative, IMHO, is to use the
assert macro instead of the exit() function. As a compromise, use the
assert macro in conjunction with the exit() function. For example:

assert(tmp != NULL);
if ( !tmp )
{
exit(EXIT_FAILURE);
}

If you use the assert macro, make sure you include <assert.h>.

As a developer, you should know to compile and test your code without
the NDEBUG macro defined. That way, the assert macro will fire off
before your program even gets to the exit() statement. Furthermore,
the assert macro will hopefully and most likely provide you with
valuable information that helps you to trace the root cause of your
problem, which is most likely, IMHO, a programming error. (If you're
really lucky, you'll be able to break into a debugger when the assert
macro fires off. Visual Studio 98 and later provide this feature,
BTW.)

Note that even with the added assert statement, you can get back to
your original functionality of calling only exit() and not assert'ing
simply by defining the macro NDEBUG; this is well defined by the C
standard.

On a somewhat related note, you should NEVER call the exit() function
from main(). Doing so expresses your lack of knowledge of Standard C,
which guarantees that a return statement in main() has the same effect
as calling exit() with an argument that is the same as the return
value. In other words, use only return statements in main()--never
call exit() from main().

And finally, the only acceptable return values from main, and the only
values you can pass into exit(), are 0, EXIT_SUCCESS and EXIT_FAILURE.
The latter two macros are defined in <stdlib.h>, so make sure to
include that header file if you use either one. Returning a value of 0
or calling exit(0) is equivalent to returning a value of EXIT_SUCCESS
or calling exit(EXIT_SUCCESS), as far as the C standard is concerned.

One convention I've grown accustomed to is to return 0 from main() if
I never return a failure condition, e.g.:

int main(void)
{
return 0;
}

But I return EXIT_SUCCESS as a successful return value if I also
return a failure condition (which can only be EXIT_FAILURE and nothing
else) from main(), e.g.:

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if ( argc < 2 )
{
printf("Error.\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

The above could arguably be better written as (along with many other
variants):

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int status = EXIT_SUCCESS;
if ( argc < 2 )
{
printf("Error.\n");
status = EXIT_FAILURE;
}
return status;
}

Best regards
 
P

Philip Potter

jaysome said:
Nobody mentioned this so far, but I think it's worth mentioning.

Your immediate above comment is wrong. The exit() function does not
necessarily print an error. In this case, the exit() function
terminates the program and, according to the C standard, allowably
silently.

In fact, most implementations I've come across don't print anything
out as a result of calling the exit() function--the program simply
terminates silently, and the user is left waiving his or her hands in
the air.

<snip>

Yes, I know all this; the comment was to show the /intent/ of that
conditional in a code example which was studying something else -
namely, realloc()ing a buffer.
If you do something like the above, make sure you include <stdio.h>
for the prototype for fprintf() and <stdlib.h> for the prototype for
exit() and the definition of the macro EXIT_FAILURE.

Similarly, I didn't show headers because I was asking questions about
concepts rather than "Why doesn't this compile?".
One of the problems with outputting an error message to stderr (or
stdout) and then calling exit() is that your user may never see the
error message. There is nothing in the C standard that prevents an
operating system, or more appropriately, a run time environment, from
terminating your program when exit() is called without you having a
glimmer of a chance of viewing the message output to stderr (or
stdout).

True. But I'm not writing production code. If I were, I would likely be
within some sort of framework which provides better error reporting and
would wrap fprintf(stderr) or CreateErrorWindow() in some sort of
errprint() function, as I have done here. I didn't quote errprint()
because it wasn't relevant. (In this case, it calls the nonstandard
function xil_printf() on the MicroBlaze soft processor, or
fprintf(stderr) in a UNIX environment.)
When you decide to call the function exit(), you are basically, as a
programmer, throwing your hands up in the air (and most likely to have
the user emulate you) and claiming that "this condition should never
happen". After all, the exit() function simply terminates the program
as far as the user is concerned.

Either that, or you're stating "if this condition does happen, this
program cannot reasonably continue". Which in this case is true. And
exit() is fine for my requirements, because this is not production code.
(Even if it was, I *still* think exit() on *alloc()-failure is the best
option for this particular application. The data generated in the buffer
is JPEG data, and a partly-generated JPEG bitstream just results in an
ugly mess and a disappointed user.)
Given this, perhaps you should consider some alternatives to using or
not using exit(). Far better as an alternative, IMHO, is to use the
assert macro instead of the exit() function. As a compromise, use the
assert macro in conjunction with the exit() function. For example:

assert(tmp != NULL);
if ( !tmp )
{
exit(EXIT_FAILURE);
}

If you use the assert macro, make sure you include <assert.h>.

I already know about assert(), but I feel that assert() is for
conditions which the programmer believes can never happen, but which
during development all too often do. That way, it is theoretically
*safe* to turn off assert()s in production code with NDEBUG because
these conditions "can't happen".

Because we know that calls to *alloc() _can_ fail, we should not use
assert() to ensure they don't - far better to detect the error and
report it through the normal channels, even during development. This way
you have tested the error-reporting functionality, assuming the
out-of-memory error occurs during development.
As a developer, you should know to compile and test your code without
the NDEBUG macro defined. That way, the assert macro will fire off
before your program even gets to the exit() statement. Furthermore,
the assert macro will hopefully and most likely provide you with
valuable information that helps you to trace the root cause of your
problem, which is most likely, IMHO, a programming error. (If you're
really lucky, you'll be able to break into a debugger when the assert
macro fires off. Visual Studio 98 and later provide this feature,
BTW.)

That would be nice if I was programming for an environment which Visual
Studio and friends target.

On a somewhat related note, you should NEVER call the exit() function
from main(). Doing so expresses your lack of knowledge of Standard C,
which guarantees that a return statement in main() has the same effect
as calling exit() with an argument that is the same as the return
value. In other words, use only return statements in main()--never
call exit() from main().

What? Why? This "NEVER" seems highly peculiar. Yes, exit() and return
are equivalent in main(). But you don't say why you should prefer
return. If it's because return is likely to be faster than exit(), this
laughable because exit() can only be called once, so the time saved is
completely insignificant.

If you like your error reporting to be idiomatic and consistent in
style, surely it's better to call exit() from main() just like you would
anywhere else?

In any case, I'd reserve the word "NEVER" for things like "NEVER free()
the same pointer twice" or "NEVER declare main() as returning void", not
stylistic points like this.
And finally, the only acceptable return values from main, and the only
values you can pass into exit(), are 0, EXIT_SUCCESS and EXIT_FAILURE.
The latter two macros are defined in <stdlib.h>, so make sure to
include that header file if you use either one. Returning a value of 0
or calling exit(0) is equivalent to returning a value of EXIT_SUCCESS
or calling exit(EXIT_SUCCESS), as far as the C standard is concerned.

Yes, I know. That's why I wrote exit(EXIT_FAILURE) and not exit(1).

<snip more>

Phil
 
C

cr88192

Flash Gordon said:
cr88192 wrote, On 23/08/07 00:03:

It is not as obvious. I can read shifts, but for something like the above
I then have to think to know what the scaling factor is, but with division
the scaling factor is actually stated. I also don't have to worry about
precedence because for arithmetic C just follows the rules I was taught
before I saw my first computer. I also don't have to worry about whether
it is a signed number (shifting a negative number is not required to act
as division). Is is also easier to change the factor with division.

Finally, and most importantly, a division expresses the intent, shift
expresses a way of achieving that intent. It is always better to express
your intent where possible until you have *proved* you need to worry about
the details.

after maybe a few years of experience, one has probably forgotten about the
issue anyways, the shift seems intuitive enough...

You missed it because you insist on looking for micro-optimisations
instead of trying to express your intent clearly. Oh wait, that is another
way of saying it makes you look stupid. This is because this sort of
micro-optimisation has been stupid for many years because the compiler
will do it for you so wasting your time trying to beet the compiler is
stupid.

not optimizations. shifts are how one typically does these things...

it is much the same as why we call int variables i, j, and k I think, or
many other common practices. after enough years, and enough code, one
largely forgets any such reasoning, all rote response really...

actually, had I been thinking of compiler behavior much at all, I would have
realized that 'i/3' actually becomes a fixed point multiply by a reciprocal.
no such reasoning was used in this case.

this was a trivial and obvious mistake is all.

I can't think of a good reason for wanting less than decent performance.
Of course, I consider all resources when thinking about performance, so
tell me what I've missed.

4/3 is a natural growth curve. something around this ratio should presumably
work good as a general mean case.

 
P

Philip Potter

cr88192 said:
4/3 is a natural growth curve. something around this ratio should presumably
work good as a general mean case.

What do you mean by this? What is a "natural growth curve"? And why is
4/3 more "natural" than, say, Euler's number or the golden ratio?
 
F

Flash Gordon

cr88192 wrote, On 23/08/07 11:12:
after maybe a few years of experience, one has probably forgotten about the
issue anyways, the shift seems intuitive enough...



not optimizations. shifts are how one typically does these things...

Not if one is being sensible.
it is much the same as why we call int variables i, j, and k I think, or
many other common practices. after enough years, and enough code, one
largely forgets any such reasoning, all rote response really...

I have spent years programming in assembler where I would use shifts and
years spent programming in high level languages where I would not.
actually, had I been thinking of compiler behavior much at all, I would have
realized that 'i/3' actually becomes a fixed point multiply by a reciprocal.
no such reasoning was used in this case.

this was a trivial and obvious mistake is all.

A trivial mistake that does not get made if you code what you want to
express instead of trying to use tricks. That is part of the point.

Whatever the reason for you learning to use such tricks it is well past
time you learned not to use them excpet where it is proved that you need to.
4/3 is a natural growth curve. something around this ratio should presumably
work good as a general mean case.

Exponential is also a natural growth curve, if you don't believe me
check how populations grow in nature, for at least some it is
exponential until a crash.

The best growth curve depends on the situation. For some thing I know
that in the foreseeable future I need space for 10 foos and if it grows
beyond that it will be unlikely to be by much, so I start with 10 and
use a small linear growth (saves having to revisit the code unless
something very strange happens). For other things that would be
completely stupid.
 
R

Richard Heathfield

Philip Potter said:
jaysome wrote:


Either that, or you're stating "if this condition does happen, this
program cannot reasonably continue".

I can only think of one situation where that might be true - an
inability to write to some kind of user-visible error channel (e.g.
stderr or hWnd or whatever other bizarre platform-specific channel is
available). Even then, it's easy enough to get back to main and return
from there - but this is a style point, of course.

I have just conducted a search of my development archives, and found a
few calls to exit() in ancient code, so I can't claim I never use it.
But without looking it up, I can't recall the last time I used it, at
least not in "real" code.


[ My dear jaysome, that's a terrible use of assert! ]
I already know about assert(), but I feel that assert() is for
conditions which the programmer believes can never happen, but which
during development all too often do. That way, it is theoretically
*safe* to turn off assert()s in production code with NDEBUG because
these conditions "can't happen".

Hear hear.
Because we know that calls to *alloc() _can_ fail, we should not use
assert() to ensure they don't - far better to detect the error and
report it through the normal channels, even during development. This
way you have tested the error-reporting functionality, assuming the
out-of-memory error occurs during development.

Right again.

What? Why? This "NEVER" seems highly peculiar. Yes, exit() and return
are equivalent in main(). But you don't say why you should prefer
return. If it's because return is likely to be faster than exit(),
this laughable because exit() can only be called once, so the time
saved is completely insignificant.

My own reason for favouring return is simply that I only ever quit from
main nowadays. At that point, there's no particular difference between
exit(n) and return n, so exit() adds no value to me whatsoever. So, as
a rule of thumb, I just don't ever bother to call exit(). But it's
purely a style thing.
If you like your error reporting to be idiomatic and consistent in
style, surely it's better to call exit() from main() just like you
would anywhere else?

Well, exit() doesn't report errors, but I do see your point about
consistency, which is why I favour return over exit. :)
In any case, I'd reserve the word "NEVER" for things like "NEVER
free() the same pointer twice" or "NEVER declare main() as returning
void", not stylistic points like this.

Careful. You might get the same pointer value twice from malloc:

p = malloc(n * sizeof *p);
store_object_representation(pobjrep, &p);
free(p);
q = malloc(n * sizeof *q);
store_object_representation(qobjrep, &q);
d = compare_object_representation(pobjrep, qobjrep);

d might well be 0. Nevertheless, it is still correct to pass q's value
to free() when you're done with it.

And of course there are circumstances where you cannot avoid declaring
main as returning void (e.g. where it is mandated in the documentation
for a freestanding implementation).

Bottom line: never say 'never' (except when saying "never say 'never'").

<snip>
 
C

cr88192

Philip Potter said:
What do you mean by this? What is a "natural growth curve"? And why is 4/3
more "natural" than, say, Euler's number or the golden ratio?

good mystery...
I don't know where this came from...


one would probably need to run simulations or something to determine this
(aka, 'what is the best growth curve').

well, in the past, I usually used 50%, or 3/2, probably good enough...

 
P

Philip Potter

Richard said:
Philip Potter said:

I can only think of one situation where that might be true - an
inability to write to some kind of user-visible error channel (e.g.
stderr or hWnd or whatever other bizarre platform-specific channel is
available). Even then, it's easy enough to get back to main and return
from there - but this is a style point, of course.

I have just conducted a search of my development archives, and found a
few calls to exit() in ancient code, so I can't claim I never use it.
But without looking it up, I can't recall the last time I used it, at
least not in "real" code.

So do you prefer to handle errors by returning an error code, which
main() can deal with as it pleases? I guess that makes sense, but it
seems like a more effortful design style than I really need for this
project.

Careful. You might get the same pointer value twice from malloc:

p = malloc(n * sizeof *p);
store_object_representation(pobjrep, &p);
free(p);
q = malloc(n * sizeof *q);
store_object_representation(qobjrep, &q);
d = compare_object_representation(pobjrep, qobjrep);

d might well be 0. Nevertheless, it is still correct to pass q's value
to free() when you're done with it.

I thought someone might come up with that example. But it could be
argued that q is a different pointer from p, because although it may
have the same value, it serves a different purpose.
And of course there are circumstances where you cannot avoid declaring
main as returning void (e.g. where it is mandated in the documentation
for a freestanding implementation).

Yes, I thought that one might come up too.
 
C

CBFalconer

Richard said:
.... snip ...

Careful. You might get the same pointer value twice from malloc:

p = malloc(n * sizeof *p);
store_object_representation(pobjrep, &p);
free(p);

After which dereferencing p is undefined behaviour.
q = malloc(n * sizeof *q);
store_object_representation(qobjrep, &q);
d = compare_object_representation(pobjrep, qobjrep);

d might well be 0. Nevertheless, it is still correct to pass q's value
to free() when you're done with it.

So what? Who cares if q == oldvalueof(p). You seem to have
something mixed up.
 
C

cr88192

Flash Gordon said:
cr88192 wrote, On 23/08/07 11:12:


Not if one is being sensible.


I have spent years programming in assembler where I would use shifts and
years spent programming in high level languages where I would not.

yeah, I also use assembler...

otherwise:

I always used shifts, never thought much of it.
I use shifts where I think shifts, I had never thought to think divides...


if I were thinking of a divide, than i/3 is an obvious difference from i/2.
if I was not, it is not.
it is a non-obvious jump from 'i>>1' to 'i/3', unless one first thinks that
'i>>1' is actually 'i/2'...

A trivial mistake that does not get made if you code what you want to
express instead of trying to use tricks. That is part of the point.

'want to express'?...

this implies certain things, ie, that what I would want to express is
different from the code I would write to express it.

do you ask me, if my grammar is unusual, if it is because I am not writing
what I am meaning to express?...

it is a similar question IMO...

Whatever the reason for you learning to use such tricks it is well past
time you learned not to use them excpet where it is proved that you need
to.

'tricks'?...

shifts are a basic operator, I don't see why they would be viewed as any
kind of trick...

do we call pointers and casts tricks as well?... typical code is riddled
with the things, nothing sinister there...

now, does one think in terms of arithmetic ops or in terms of bitwise
ops?...
does one do their everyday arithmetic (internally) in decimal or in hex?...

if we read something do we see the text, hear words, or see imagery?...


this kind of issue has not come up in any time in recent memory...


so, one thinks in hex and writes in decimal, or vice versa, not usually that
important.
and, if one happens to be thinking in hex right, then a shift is more
intuitive than a divide.

does one think of the money in their wallet as 300 or 0x12C?...
does one think in british or metric units?...

or does one use whatever system happens to seem more natural at that
instant?...


up to them really. it only matters that when someone asks that they give the
right number, and when they read that they don't get confused...

one typically doesn't know the magic going on even in ones' own head...

Exponential is also a natural growth curve, if you don't believe me check
how populations grow in nature, for at least some it is exponential until
a crash.

when applied recursively, this is exponential...
 
R

Richard Heathfield

Philip Potter said:
Richard Heathfield wrote:

So do you prefer to handle errors by returning an error code, which
main() can deal with as it pleases?
Yes.

I guess that makes sense, but it
seems like a more effortful design style than I really need for this
project.

<shrug> I do it that way not because it takes effort but because it
saves effort. At least, it does for me. By sticking to a rigid
structure, I find it much, much easier to locate and destroy bugs.

I thought someone might come up with that example. But it could be
argued that q is a different pointer from p, because although it may
have the same value, it serves a different purpose.

Indeed it could be argued that the second pointer is a different value
that just happens to look remarkably similar to the first. I was, of
course, merely cranking up the pedantry. :)
 
P

Philip Potter

Richard said:
Philip Potter said:

Indeed it could be argued that the second pointer is a different value
that just happens to look remarkably similar to the first. I was, of
course, merely cranking up the pedantry. :)

I think you cranked it up a long time ago; the wind changed and it got
stuck...
 
F

Flash Gordon

cr88192 wrote, On 23/08/07 16:09:
yeah, I also use assembler...

otherwise:

I always used shifts, never thought much of it.
I use shifts where I think shifts, I had never thought to think divides...

if I were thinking of a divide, than i/3 is an obvious difference from i/2.
if I was not, it is not.
it is a non-obvious jump from 'i>>1' to 'i/3', unless one first thinks that
'i>>1' is actually 'i/2'...

You are trying to reduce a number by a factor not trying to move bits.
They are conceptually different things. Try using a shit to halve a
floating point number as see how far it gets you.
'want to express'?...

this implies certain things, ie, that what I would want to express is
different from the code I would write to express it.

Shift is for moving bits, divide is for scaling. Otherwise shift would
work on floating point and would have behaviour defined by the C
standard for negative numbers (it leaves it for the implementation to
define the result of right shifting a negative number).
do you ask me, if my grammar is unusual, if it is because I am not writing
what I am meaning to express?...

it is a similar question IMO...

No, it is a question of using the wrong word. One that in some
situations happens to be similar, but in many situations means something
completely different.
'tricks'?...

Yes, it is a trick.
shifts are a basic operator, I don't see why they would be viewed as any
kind of trick...

The shift operator is a basic operator for moving bits, using it to
divide is a trick and one that does not work in all situations.
do we call pointers and casts tricks as well?... typical code is riddled
with the things, nothing sinister there...

Code riddled with casts probably is bad.

Code using a lot of shifts for shifting bits, NOT for division, could be
good.
now, does one think in terms of arithmetic ops or in terms of bitwise
ops?...

No. However a shift works in terms of bits, not in terms of arithmetic.
does one do their everyday arithmetic (internally) in decimal or in hex?...

if we read something do we see the text, hear words, or see imagery?...

Several of the things above are reasons for using divide rather than shift.
this kind of issue has not come up in any time in recent memory...

The issue of you making a mistake that you would not have done had you
stuck to doing simple integer division is a good reason for using
integer division.
so, one thinks in hex and writes in decimal, or vice versa, not usually that
important.
and, if one happens to be thinking in hex right, then a shift is more
intuitive than a divide.

What do you get if you right shift -1? On some machines it will be 32767
on others it will be -1. Does that sound like division to you?
does one think of the money in their wallet as 300 or 0x12C?...
does one think in british or metric units?...

or does one use whatever system happens to seem more natural at that
instant?...

Not relevant since a shift is not a different representation for
division, it is a different operation.
up to them really. it only matters that when someone asks that they give the
right number, and when they read that they don't get confused...

one typically doesn't know the magic going on even in ones' own head...

Code is written for other people, not just the original author or the
compiler.
when applied recursively, this is exponential...

Linear is another natural growth curve.

Ah, so you realise now that suggesting a 4/3 curve makes less sense than
suggesting using a curve that will give good performance. My example
above was extreme, but there are plenty of less extreme examples.
 
S

santosh

cr88192 said:
Flash Gordon said:
cr88192 wrote, On 23/08/07 11:12:
not optimizations. shifts are how one typically does these things...

Not if one is being sensible. [ ... ]
I have spent years programming in assembler where I would use shifts and
years spent programming in high level languages where I would not.

yeah, I also use assembler...

otherwise:

I always used shifts, never thought much of it.
I use shifts where I think shifts, I had never thought to think divides...
Whatever the reason for you learning to use such tricks it is well past
time you learned not to use them excpet where it is proved that you need
to.
'tricks'?...

shifts are a basic operator, I don't see why they would be viewed as any
kind of trick...

The point, I think, is that using right shift for achieving the effect of
division is not completely portable. It used to be a viable alternative at
a time when optimisation was primitive, but it's useless and flaky
nowadays, since almost any compiler is going to optimise a division into
shifts, if it can.
do we call pointers and casts tricks as well?

Not the devices themselves, but their uses in specific cases, eg., type
punning, casts that discard data etc.
... typical code is riddled
with the things, nothing sinister there...

now, does one think in terms of arithmetic ops or in terms of bitwise
ops?...

No, in terms of their effect according to the rules of arithmetics. Numbers
may be represented as bits under computers, but that is no reason to think
of every arithmetic operation in terms of their effect at the bit
representation level, except when it _is_ required.
does one do their everyday arithmetic (internally) in decimal or in
hex?...

if we read something do we see the text, hear words, or see imagery?...

Personally, an amalgamation of all three, plus imagination of other types of
sensory inputs, as appropriate.

<snip>
 
C

CBFalconer

Flash said:
cr88192 wrote, On 23/08/07 16:09: .... snip ...


Yes, it is a trick.


The shift operator is a basic operator for moving bits, using it
to divide is a trick and one that does not work in all situations.

Actually, the shift operator is defined in terms of division or
multiplication, IIRC. This is just one more reason it should not
be used on negative values. Use of unsigned is safe.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top