Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Re: Formatting a number in a different way

Reply
Thread Tools

Re: Formatting a number in a different way

 
 
David Hutto
Guest
Posts: n/a
 
      01-20-2011
On Jan 20, 12:24*pm, Seebs <(E-Mail Removed)> wrote:
> On 2011-01-20, Mark Bluemel <(E-Mail Removed)> wrote:
>
> > "Lying" is a rather strong term

>
> But in context, self-referential.
>
> > it's hard to lie about the
> > nature/purpose of comp.lang.c when there is no formal definition of it.

>
> You could easily lie about it; all you'd have to do is have an opinion
> about the nature or purpose of the group, then say something contrary to
> that opinion.
>
> > Empirically I'd suggest the observable nature of the newsgroup is more
> > discursive than problem-solving.

>
> I'd mostly think so, but...
>
> I guess I'd say I think it's perfectly appropriate to ask for help in a
> discussion group. *I don't think a group being a discussion group (which
> all Usenet groups are by default unless stated otherwise, giving us our
> clear answer) precludes straight requests for help from being appropriate..
>
> However... *Many of us are experienced programmers. *One of the things
> experienced programmers have usually learned is that the words in the
> specification are frequently incorrect, because the person asking has
> misunderstood the problem they need solved. *As a result, if you come here
> asking for help, it is reasonably well justified for people to second-guess
> your request.
>
> For a specific example, if something looks like homework, most experienced
> programmers will conclude that, though the STATED problem was "I want someone
> to do this for me", the REAL problem is "I need to learn how to do this so
> I can not just get a passing grade but get one honestly".


But this is assuming that all individuals here that ask a basic
question are in school. We're all self learners, even when in class we
have to comprehend the outer layers of education in the current
curriculum. So don't always assume a basic question is 'school' level,
when it might be just 'tutorial' level.

>
> That said, I didn't think the original looked like homework. *It did, however,
> look like a fairly arcane spec, and I'm still not sure what the intended
> functionality is.
>
> -s
> --
> Copyright 2010, all wrongs reversed. *Peter Seebach / (E-Mail Removed)://www.seebs.net/log/<-- lawsuits, religion, and funny pictureshttp://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
> I am not speaking for my employer, although they do rent some of my opinions.


 
Reply With Quote
 
 
 
 
Seebs
Guest
Posts: n/a
 
      01-20-2011
On 2011-01-20, David Hutto <(E-Mail Removed)> wrote:
>> For a specific example, if something looks like homework, most experienced
>> programmers will conclude that, though the STATED problem was "I want someone
>> to do this for me", the REAL problem is "I need to learn how to do this so
>> I can not just get a passing grade but get one honestly".


> But this is assuming that all individuals here that ask a basic
> question are in school.


No, it isn't. I didn't state that "basic questions" were the same category
as "looks like homework".

> We're all self learners, even when in class we
> have to comprehend the outer layers of education in the current
> curriculum. So don't always assume a basic question is 'school' level,
> when it might be just 'tutorial' level.


Right...

>> That said, I didn't think the original looked like homework.


See?

Not all newbie questions look like homework. In fact, in my experience,
not that many do; homework problems have a characteristic artificiality to
them, in that they're built around a result you can easily verify without
using the computer, rather than something you'd actually need a computer
to do.

-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / http://www.velocityreviews.com/forums/(E-Mail Removed)
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
I am not speaking for my employer, although they do rent some of my opinions.
 
Reply With Quote
 
 
 
 
Bartc
Guest
Posts: n/a
 
      01-20-2011

"pozz" <(E-Mail Removed)> wrote in message
news:ihack6$1go$(E-Mail Removed)...
> Il 20/01/2011 11:46, Mark Bluemel ha scritto:
>> I find the basic idea below easier to comprehend. I haven't tried to
>> achieve all you have, but I think the idea is clear enough and could
>> be extended to do what you want.
> > [...]

>
> This an interesting approach, but I couldn't find how I can achieve the
> zero-padding, the sign printing and the custom width.
>
> Also the split() function uses many divisions and multiplications in
> cycles (these are time consuming operations).
> I'd like to call just the sprintf() to have all the digits in textual form
> and operate only character shifting or similar things.


You might find that sprintf() also uses division internally.

You're making things a little harder (IMO) by using the C-style format
specification for sprintfnum(). This is fiddly to decipher.

Perhaps forget that to start with (it can be bolted on later), and
concentrate on the 3 parameters that the format would yield:

Width, Leadingzeros, and Plussign (W,Z and P)

You apply sprintf() (or even itoa()) to the data x, to give a string, and
then there is div, effectively a fourth parameter (DP), which just gives the
offset of the decimal point within that string.

Now the logic is a bit simpler: apply W, Z, P and DP to the plain string
version of x. This is not exactly trivial yet, depending on how many things
you want to take care of (what happens when W is too small, what if DP
yields a point outside the field, when to suppress the decimal point, and so
on.)

--
Bartc



 
Reply With Quote
 
Bartc
Guest
Posts: n/a
 
      01-20-2011

"pozz" <(E-Mail Removed)> wrote in message
news:ihabfg$e2$(E-Mail Removed)...
> Il 20/01/2011 18:24, Seebs ha scritto:
>> [...] It did, however,
>> look like a fairly arcane spec, and I'm still not sure what the intended
>> functionality is.

>
> It is very simple. I'm using a basic 16-bit microprocessor with limited
> code memory. I don't want to use floating point variables, because they
> are very time and size consuming.


Your approach, using a signed integer plus (effectively) an exponent field,
*is* floating point...

However your 16+16-bit format yields 15 bits of precision, plus a too-big
exponent range, while normal 32-bit floating point typically gives 23 bits
of precision, and an 8-bit exponent range. And combined in a way to make it
easy to compare, assign and so on.

The format also differs from yours, in that the 'integer' part is usually
normalised to represent a number in the range 1.0 to 1.999999...

Your format I believe would have multiple representations for numbers:
(123,1) is 1230, but so is (1230,0).

So using an established standard format (IEEE floating point) would have
benefits, even if you don't do any actual floating point arithmetic on the
numbers.

(BTW if you don't care about the standard formats, but like to save memory,
then 16+8-bit format would work almost as well, if your microprocessor is
byte-addressable,)

--
Bartc




 
Reply With Quote
 
Seebs
Guest
Posts: n/a
 
      01-21-2011
On 2011-01-20, pozz <(E-Mail Removed)> wrote:
> This is the reason why I use int variables to store the significant
> digits of all the signals even with different resolutions. But I need to
> know the resolution (divisor/multipler) together with the significant
> digits to output a human readable value.


> This is the reason to have a function that converts the couple (value,
> div) in a formatted string.


Makes sense! Hmm.

/* printnum: print a string of digits into a buffer,
* optionally with magnitude used as a fixed-point
* denominator.
* magnitude is the log(10) of the number; thus,
* 123, 1 => 1230
* 123, 0 => 123
* 123, -1 => 12.3
*/
int
printnum(char *buf, size_t size, int digits, int magnitude) {
size_t chars;
int i;

chars = snprintf(buf, size, "%d", digits);
if (chars > size) {
fprintf(stderr, "Number too big.\n");
return 1;
}
if (magnitude > 0) {
if (chars + magnitude > (size - 1)) {
fprintf(stderr, "Number plus multiplier too big.\n");
return 1;
}
for (i = 0; i < magnitude; ++i)
buf[chars + i] = '0';
buf[chars + i] = '\0';
return 0;
} else if (magnitude < 0) {
magnitude *= -1;
if (magnitude > chars) {
if (magnitude + 1 > (size - 1)) {
fprintf(stderr, "No space for leading zeroes.\n");
return 1;
}
memmove(buf + 1 + (magnitude - chars), buf, chars + 1);
buf[0] = '.';
for (i = 0; i < (magnitude - chars); ++i)
buf[i + 1] = '0';
return 0;
} else {
if (chars + 1 > (size - 1)) {
fprintf(stderr, "No space for decimal.\n");
return 1;
}
memmove(buf + chars - magnitude + 1, buf + chars - magnitude, magnitude + 1);
buf[chars - magnitude] = '.';
return 0;
}
} else {
return 0;
}
}

.... The rationale of this design is that it basically separates things into
the question of what the digits are, and the question of where the decimal
point goes. There's probably a prettier way to do this. I suppose you
could handle negative magnitudes quite nicely with:

int ten2the = 1;
for (i = 0; i < magnitude; ++i)
ten2the *= 10;
printf("%d.%0*d", (digits / ten2the), magnitude, (digits % ten2the));

That might be saner.

Anyway, there's a couple of ideas, maybe one of them will get you somewhere.

-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / (E-Mail Removed)
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
I am not speaking for my employer, although they do rent some of my opinions.
 
Reply With Quote
 
Ian Collins
Guest
Posts: n/a
 
      01-21-2011
On 01/21/11 05:45 PM, Seebs wrote:
> On 2011-01-20, pozz<(E-Mail Removed)> wrote:
>> This is the reason why I use int variables to store the significant
>> digits of all the signals even with different resolutions. But I need to
>> know the resolution (divisor/multipler) together with the significant
>> digits to output a human readable value.

>
>> This is the reason to have a function that converts the couple (value,
>> div) in a formatted string.

>
> Makes sense! Hmm.
>
> /* printnum: print a string of digits into a buffer,
> * optionally with magnitude used as a fixed-point
> * denominator.
> * magnitude is the log(10) of the number; thus,
> * 123, 1 => 1230
> * 123, 0 => 123
> * 123, -1 => 12.3
> */
> int
> printnum(char *buf, size_t size, int digits, int magnitude) {
> size_t chars;
> int i;
>
> chars = snprintf(buf, size, "%d", digits);
> if (chars> size) {
> fprintf(stderr, "Number too big.\n");
> return 1;
> }
> if (magnitude> 0) {
> if (chars + magnitude> (size - 1)) {
> fprintf(stderr, "Number plus multiplier too big.\n");
> return 1;
> }
> for (i = 0; i< magnitude; ++i)
> buf[chars + i] = '0';
> buf[chars + i] = '\0';
> return 0;
> } else if (magnitude< 0) {
> magnitude *= -1;
> if (magnitude> chars) {
> if (magnitude + 1> (size - 1)) {
> fprintf(stderr, "No space for leading zeroes.\n");
> return 1;
> }
> memmove(buf + 1 + (magnitude - chars), buf, chars + 1);
> buf[0] = '.';
> for (i = 0; i< (magnitude - chars); ++i)
> buf[i + 1] = '0';
> return 0;
> } else {
> if (chars + 1> (size - 1)) {
> fprintf(stderr, "No space for decimal.\n");
> return 1;
> }
> memmove(buf + chars - magnitude + 1, buf + chars - magnitude, magnitude + 1);
> buf[chars - magnitude] = '.';
> return 0;
> }
> } else {
> return 0;
> }
> }
>
> .... The rationale of this design is that it basically separates things into
> the question of what the digits are, and the question of where the decimal
> point goes. There's probably a prettier way to do this. I suppose you
> could handle negative magnitudes quite nicely with:
>
> int ten2the = 1;
> for (i = 0; i< magnitude; ++i)
> ten2the *= 10;
> printf("%d.%0*d", (digits / ten2the), magnitude, (digits % ten2the));
>
> That might be saner.


I tackled (without any library functions) this by first breaking down
the format string into one of these:

typedef struct format_t
{
const char* before;
const char* after;
int beforeSize;
int afterSize;
int width;
char sign;
char pad;
} Format;

Then formatting the number thus:

char* formatNumber( int number, int exponent, const Format* format )
{
static char str[16];
char* p = str+sizeof(str)-1;

*p-- = '\0';

const char* const end = p;

if( exponent >= 0 )
{
while( exponent-- ) *p-- = '0';

while( number )
{
*p-- = '0'+(number%10);
number /= 10;
}
}
else /* negative exponent */
{
while( number )
{
*p-- = '0'+(number%10);
number /= 10;

if( ++exponent == 0 )
*p-- = '.';
}
}

return decorateNumber( p, end, format );
}

--
Ian Collins
 
Reply With Quote
 
Bartc
Guest
Posts: n/a
 
      01-21-2011
"Bartc" <(E-Mail Removed)> wrote in message
news:ihahei$im9$(E-Mail Removed)-september.org...
>
> "pozz" <(E-Mail Removed)> wrote in message
> news:ihack6$1go$(E-Mail Removed)...


> You're making things a little harder (IMO) by using the C-style format
> specification for sprintfnum(). This is fiddly to decipher.
>
> Perhaps forget that to start with (it can be bolted on later), and
> concentrate on the 3 parameters that the format would yield:
>
> Width, Leadingzeros, and Plussign (W,Z and P)


How is one implementation that appears to work. Although your spec is rather
ambiguous. It directly prints the number rather than return a string; and
expects all the parameters (width, etc) to have been previously obtained
(since I think that parsing the format string properly is actually a bigger
task):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void emit(char c) { printf("%c",c);}

/* X is base number
Dp (Div) is x10 scale index (-2 -1 0 1 2 => /100 /10 *1 *10 *100)
Width is minimum width (no error if exceeded)
Plus is 0, or 1 or '+' to force sign
Zeropad is 1 to have leading zeros on left
No decimal point unless dp is negative
Output goes directly to stdout via emit()
*/

void formatnum(int x,int dp, int width,int plus,int zeropad) {
int asign=0,bsign=0,padding=0;
int zerodot=0,dotzeros=0,trailzeros=0,middot=0,xlen;
char str[20];
int i,m;

xlen=sprintf(str,"%d",abs(x));

if (x<0 || (x>=0 && plus))
if (zeropad) asign=1; else bsign=1;

if (dp>0)
trailzeros=dp;
else if (dp<0) {
m=-dp;
if (m>=xlen) {
zerodot=1;
dotzeros=(m-xlen);
}
else
middot=m;
}

padding=width-(zerodot*2+dotzeros+bsign+(middot?1:0)+xlen+trailz eros)-asign;
if (padding<0) padding=0;

if (asign) emit(x<0?'-':'+');
for (i=1; i<=padding; ++i) emit(zeropad?'0':' ');
if (bsign) emit(x<0?'-':'+');
if (zerodot) {emit('0'); emit('.');}
for (i=1; i<=dotzeros; ++i) emit('0');
for (i=0; i<xlen; ++i) {
emit(str[i]);
if (middot && (xlen-i-1==middot)) emit('.');
}
for (i=1; i<=trailzeros; ++i) emit('0');
}

int main(void){
char* s;
int dp;

for (dp=-5; dp<5;++dp) {
printf("<");
formatnum(-123,dp, 10,0,0);
printf(">\n");
}

}

--
Bartc


 
Reply With Quote
 
Bartc
Guest
Posts: n/a
 
      01-23-2011

"pozz" <(E-Mail Removed)> wrote in message
news:ihibmb$rut$(E-Mail Removed)...
> Il 20/01/2011 20:58, Bartc ha scritto:
>> Your approach, using a signed integer plus (effectively) an exponent
>> field,
>> *is* floating point...

>
> Hmmm..., I think my approach is fixed point rather than floating point.
> When I set the exponent of 10 (divisor or multiplier), the number is
> fixed-point.


No. With fixed point, you don't store the exponent, divisor, multiplier or
whatever you call it. The position of the decimal point is always in the
same (fixed) place for values of the same fixed point type.

As soon as you start adding a variable multiplier, then it becomes floating
point (because the position of the decimal point is no longer fixed).

> After reading Wikipedia article about fixed point,


Then that should have made it clearer.

> I understand this type of format has some reason in many cases, above all
> in embedded applications where FPU is not present... and this is my case.
> Also, the fixed-point representation isn't affected by the problem of
> representing fractional numbers (0.1, 0.01) without errors. With floating
> point this is not possible.


I think it is, assuming your fixed point repr still uses binary. I doubt
from your op that you were using decimal or BCD format. Binary fixed-point
still has a problem with 0.1.

Your original scheme is better for this purpose, as 0.1 is represented
exactly by (1,-1). (But we don't know what the source of your data is, how
accurate or noisy it is, and whether these matters have any significance.)

> Some software (like GNUCash) uses only fixed-point variables to avoid the
> problem of rounding.


Assuming that uses a decimal format, there will always be errors; what does
it store for 1/3?

>> So using an established standard format (IEEE floating point) would have
>> benefits, even if you don't do any actual floating point arithmetic on
>> the
>> numbers.

>
> Are you sure? Operations with floating point variables (even only the
> assignment) is very time-consuming on a low-end embedded processor without
> the FPU.


That sounds unlikely. Assigning 32-bits should be independent on whether
it's an int, a float, or comprises two shorts. *Especially* if there is no
FPU which the compiler might try and use.

> Also the compiler should include a relevant size library to manage
> floating point variables.


I did this stuff when I had no compiler help. The first floating point
format I programmed used 24-bits (1+7+16-bit) on an 8-bit microprocessor.
That didn't even have integer multiply or divide, so there wasn't that great
an advantage to using the inferior fixed point rather than floating point.

And of course a lot of corners needed to be cut to get any sort of speed (no
checking for underflow, overflow for example).

It sounds like possibly your compiler is calling in a hefty library as soon
as anything is declared as a float. Unless you want to program in assembler
however, then the fixed point or scaled format you have might be the best
bet.

(Also, the IEEE format I suggested uses binary format, making scaling by
powers of 10 diffcult -- ie. slow -- even when you do this in assembler. So
forget that. Although it's not that clear now exactly what your
representation is and whether it's binary or decimal).

> Moreover, with floating point variables I can't store the simple number
> 0.1.


You can also store it as a string: "0.1", which is a kind of decimal format.

> After thinking about fixed-point arithmetic, I found that a fixed-point
> number is a "two integers" number: integer and decimal parts.
> The fractional value 1.23 can be represented as
> 123 with a scale of -100 (-100 is the factor 1/100)
> or as
> 1 (integral part) and 23 (decimal part)
> If I can convert the first representation in the second, it'll be very
> simple to printout in a textual form the two parts.
>
> So I wrote this code:


> whole = exp > 0 ? (long int)x * exp : x / -exp;


I thought you said you had a lowly 16-bit processor. Divide is usually the
slowest operation.

With your original scheme, the 'exponent' merely involved counting along a
string.

> The first difference is that the divisor/multiplier is the real
> divisor/multiplier (10, 100, 1000) and not only the exponent (1, 2, 3).


Well if it's fast enough then that's probably OK.

--
Bartc



 
Reply With Quote
 
pozz
Guest
Posts: n/a
 
      01-24-2011
On 23 Gen, 21:50, "Bartc" <(E-Mail Removed)> wrote:
> >> Your approach, using a signed integer plus (effectively) an exponent
> >> field,
> >> *is* floating point...

>
> > Hmmm..., I think my approach is fixed point rather than floating point.
> > When I set the exponent of 10 (divisor or multiplier), the number is
> > fixed-point.

>
> No. With fixed point, you don't store the exponent, divisor, multiplier or
> whatever you call it. The position of the decimal point is always in the
> same (fixed) place for values of the same fixed point type.
>
> As soon as you start adding a variable multiplier, then it becomes floating
> point (because the position of the decimal point is no longer fixed).


Yes, you'd be right if I change the multiplier with arithmetic
operations. In my application multiplier is stored in a variable, but
it remains constant during execution.
Suppose the multiplier is 1/100 (read from a non volatile memory): it
will never change.
When x=5678, the real value is 56.78. If multiply by 2, x will be
11356 and the real value will be 113.56. The decimal point always
stays after the second digits (starting from the right).
With floating-point representation, the multiplier will change
accordingly with the operations (addition, multiplication and so on).


> > I understand this type of format has some reason in many cases, above all
> > in embedded applications where FPU is not present... and this is my case.
> > Also, the fixed-point representation isn't affected by the problem of
> > representing fractional numbers (0.1, 0.01) without errors. With floating
> > point this is not possible.

>
> I think it is, assuming your fixed point repr still uses binary. I doubt
> from your op that you were using decimal or BCD format. Binary fixed-point
> still has a problem with 0.1.
>
> Your original scheme is better for this purpose, as 0.1 is represented
> exactly by (1,-1). (But we don't know what the source of your data is, how
> accurate or noisy it is, and whether these matters have any significance.)


My representation is binary of course, but the multiplier is base 10,
not base 2.


> >> So using an established standard format (IEEE floating point) would have
> >> benefits, even if you don't do any actual floating point arithmetic on
> >> the
> >> numbers.

>
> > Are you sure? Operations with floating point variables (even only the
> > assignment) is very time-consuming on a low-end embedded processor without
> > the FPU.

>
> That sounds unlikely. Assigning 32-bits should be independent on whether
> it's an int, a float, or comprises two shorts. *Especially* if there is no
> FPU which the compiler might try and use.


Ok, it is time consuming only for operations, like sprintf("%f").


> > Also the compiler should include a relevant size library to manage
> > floating point variables.

>
> I did this stuff when I had no compiler help. The first floating point
> format I programmed used 24-bits (1+7+16-bit) on an 8-bit microprocessor.
> That didn't even have integer multiply or divide, so there wasn't that great
> an advantage to using the inferior fixed point rather than floating point.
>
> And of course a lot of corners needed to be cut to get any sort of speed (no
> checking for underflow, overflow for example).


The compiler I'm using has almost full support for floating point
variables. So I think I'd use it without writing myself library code
for floating point.


> It sounds like possibly your compiler is calling in a hefty library as soon
> as anything is declared as a float. Unless you want to program in assembler
> however, then the fixed point or scaled format you have might be the best
> bet.


Yes, you got it.


> (Also, the IEEE format I suggested uses binary format, making scaling by
> powers of 10 diffcult -- ie. slow -- even when you do this in assembler. So
> forget that. Although it's not that clear now exactly what your
> representation is and whether it's binary or decimal).


It is binary, with multiplier base 10, not base 2.
So 10.2 is 102 with multiplier 1/10.


> > Moreover, with floating point variables I can't store the simple number
> > 0.1.

>
> You can also store it as a string: "0.1", which is a kind of decimal format.


Actually I have (1,1/10) and I can't change it. So I need a way to
convert it in a textual form.


> > So I wrote this code:
> > whole = exp > 0 ? (long int)x * exp : x / -exp;

>
> I thought you said you had a lowly 16-bit processor. Divide is usually the
> slowest operation.
>
> With your original scheme, the 'exponent' merely involved counting along a
> string.


Yes, I know. Anyway, as you said in another message, sprintf() uses
divisions by 10 if you have binary values (like me). So this is
another division that simplify the algorithm. I'll measure some
benchmarks.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
way way way OT: MCNGP Announcement Neil MCSE 174 04-17-2006 05:55 PM
AMD Opteron: 1-way, 2-way, ... Up to 8-way. John John Windows 64bit 12 12-27-2005 08:17 AM
OT: Number Nine, Number Nine, Number Nine Frisbee® MCSE 37 09-26-2005 04:06 PM
TURNING CRAZY, is there a way to write it in a different way? whats wrong francisco lopez Javascript 2 12-31-2004 11:15 PM
Need formatting options menu for formatting hard drive Mark T. Computer Support 3 11-24-2003 11:50 PM



Advertisments