[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

Converting Floats to Strings yields erratic results

unknown

6/7/2011 9:10:00 PM

Hey all,
I'm writing, for my own personal enlightenment, a little program
that will convert a floating-point number, passed as an argument on
the command line, into a character string. I'm now aware of
sprintf(), but I wasn't when I started writing this program, and I
feel it is my duty to finish it ;-)

Everything, in general, works fine, except that some values are
converted to one less than their actual value. For example, 15
returns 15, 14.123 returns 14, but 14 returns 13. I am _completely_
clueless, although I suspect it has something to do with rounding and
truncating happening behind my back. The important part is as
follows...

while (foo >= 10) {
foo = foo/10.0;
tenDivs++;
}

i = 0;
while (tenDivs >= 0) {
while (!(foo < 1)) {
foo--;
oneDivs++;
}
str[i] = 48 + oneDivs;
foo = foo * 10;
oneDivs = 0;
tenDivs--;
i++;
}

Where "foo" is the value to be converted. Can anybody help, or point
me to a source that can?

In an unrelated question: Does anyone know of any link which describes
the (relative) performance of all kinds of C operations? e.g: how fast is
"add" comparing with "multiplication" on a typical machine.

Thanks!
32 Answers

Keith Thompson

6/7/2011 9:25:00 PM

0

"Dirk T. Shelley" <nospam@nospam.com> writes:
> I'm writing, for my own personal enlightenment, a little program
> that will convert a floating-point number, passed as an argument on
> the command line, into a character string. I'm now aware of
> sprintf(), but I wasn't when I started writing this program, and I
> feel it is my duty to finish it ;-)

Typically you *can't* pass floating-point numbers on the command line.
You can only pass strings. Presumably you convert those strings to
floating-point numbers somehow, but you don't tell us how.

> Everything, in general, works fine, except that some values are
> converted to one less than their actual value. For example, 15
> returns 15, 14.123 returns 14, but 14 returns 13. I am _completely_
> clueless, although I suspect it has something to do with rounding and
> truncating happening behind my back. The important part is as
> follows...

If you don't underrstand what's happening, how do you know that this is
the important part?

> while (foo >= 10) {
> foo = foo/10.0;

This will yield an inexact result (and for all we know the original
value of foo was inexact). Floating-point numbers are represented in
binary, not decimal.

Suggested reading: section 14 of the comp.lang.c FAQ,
<http://www.c-fa.... For more advanced reading, search for
Goldberg's "What Every Computer Scientist Should Know About
Floating-Point Arithmetic".

> tenDivs++;
> }
>
> i = 0;
> while (tenDivs >= 0) {
> while (!(foo < 1)) {
> foo--;
> oneDivs++;
> }
> str[i] = 48 + oneDivs;

Where did the value 48 come from?

Oh, I see, it's the ASCII value of '0'. Just write '0' (yes, character
constants are of type int); it makes your code clearer and potentially
more portable. It assumes that the representations of '0' through '9'
are contiguous; as it happens, the language guarantees that.

> foo = foo * 10;
> oneDivs = 0;
> tenDivs--;
> i++;
> }
>
> Where "foo" is the value to be converted. Can anybody help, or point
> me to a source that can?

I haven't taken the time to analyze what your program is doing, but
consider this. As I mentioned above, dividing a floating-point
number x by 10.0 is likely to give you an inexact result.
Multiplying that result by 10.0 could give you something other than
the original number, and truncating that result could give you x - 1.

> In an unrelated question: Does anyone know of any link which describes
> the (relative) performance of all kinds of C operations? e.g: how fast is
> "add" comparing with "multiplication" on a typical machine.

I don't know. You can probably assume that addition is faster
than multiplication, and division is slower than either, but there
are no consistent guarantees. The relative performance of integer
and floating-point arithmetic can vary a great deal depending on
the hardware.

--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.ne...
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

gordonb.9ycz5

6/7/2011 11:13:00 PM

0

> I'm writing, for my own personal enlightenment, a little program
> that will convert a floating-point number, passed as an argument on
> the command line, into a character string. I'm now aware of

Command-line arguments *ARE* character strings, not floating-point
numbers. They might be the character-string representation of a
floating point number.

Your program seems to be chopping the floating point number to an
integer. That's not what you described above.

> sprintf(), but I wasn't when I started writing this program, and I
> feel it is my duty to finish it ;-)

Decimal numbers that aren't integers are hardly ever exactly
representable in binary floating point. If the decimal fraction
isn't .0 and doesn't end in 5 (ignoring trailing zeroes) there is
no exact representation for it. Most of the decimal fractions
ending in 5 also don't have an exact representation, either. Here
are a few that do:

0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875

Pencil and paper exercise:

1. Divide 1 by 3, to 6 decimal places. Result: 0.333333
2. Multiply the result by 3: Result: 0.999999
3. Round this down to an integer. Result: 0.0
Is the result what you expected? Does it seem to match what might
be happening in your program?

0.1 is an infinite-repeating fraction in binary, so expect this
same kind of behavior for decimal numbers.

> Everything, in general, works fine, except that some values are
> converted to one less than their actual value. For example, 15
> returns 15, 14.123 returns 14, but 14 returns 13. I am _completely_
> clueless, although I suspect it has something to do with rounding and
> truncating happening behind my back. The important part is as

Try printing the intermediate numbers with a format like this:
printf("%200.100lf\n", foo);
(where I'm assuming that foo is declared as a double).

> follows...
>
> while (foo >= 10) {
> foo = foo/10.0;

Dividing by 10 causes rounding errors.

> tenDivs++;
> }
>
> i = 0;
> while (tenDivs >= 0) {
> while (!(foo < 1)) {
> foo--;
> oneDivs++;
> }
> str[i] = 48 + oneDivs;
> foo = foo * 10;

Multiplying by 10 will not exactly undo the division by 10 done above.

> oneDivs = 0;
> tenDivs--;
> i++;
> }
>
> Where "foo" is the value to be converted. Can anybody help, or point
> me to a source that can?

Since you seem not to be trying to print the fractional amount, try
converting your number to an integer right off and do no floating-point
operations whatever.

> In an unrelated question: Does anyone know of any link which describes
> the (relative) performance of all kinds of C operations? e.g: how fast is
> "add" comparing with "multiplication" on a typical machine.

A typical G-series TI MSP430 microcontroller has no integer hardware
multiply instruction, and code for a floating-point multiply won't
fit in available code space (2k or less). I don't think that's
what you wanted to hear.

Some Pentium instruction timings:

floating-point add 3
floating-point subtract 3
floating-point multiply 3

This essentially means that the overhead of fetching the instruction and
operands for add, subtract, and multiply overwhelms the calculation time.
Multiply is generally slower than add.

floating-point divide 39
fp sine/cosine 16-126
fp square root 70

although computing instruction timings for a processor like the Pentium is
complicated enough that universities ought to give out PHDs in instruction
timing, given cache, pipelining, and a whole bunch of other variables.

Eric Sosman

6/8/2011 12:47:00 AM

0

On 6/7/2011 5:10 PM, Dirk T. Shelley wrote:
> Hey all,
> I'm writing, for my own personal enlightenment, a little program
> that will convert a floating-point number, passed as an argument on
> the command line, into a character string. I'm now aware of
> sprintf(), but I wasn't when I started writing this program, and I
> feel it is my duty to finish it ;-)
>
> Everything, in general, works fine, except that some values are
> converted to one less than their actual value. For example, 15
> returns 15, 14.123 returns 14, but 14 returns 13. I am _completely_
> clueless, although I suspect it has something to do with rounding and
> truncating happening behind my back. The important part is as
> follows...
>
> while (foo>= 10) {
> foo = foo/10.0;

This line is "behind your back." On nearly every computer these
days, floating-point numbers use a base-two representation. A few
use base-sixteen, but the days of base-ten floating-point are pretty
much over and gone. (You'll still find it in hand-held calculators,
but you'll be hard-pressed to find it anywhere else.)

Consequence: You're dividing by 10, which is to say you're dividing
by 2*5. The 2 makes no trouble for a base-two system (and only a little
and subtler trouble for base-sixteen), but the 5 is a major problem.
It's very much like dividing by 35=5*7 in decimal: The 5 is no problem,
but the 7 is likely to involve an infinite repeating decimal fraction.
But just as you lack the patience to write out an infinite number of
fraction digits, the computer lacks the patience (and memory space) to
do the same. So you both stop writing digits after a while, round off
the result, and content yourselves with an approximate answer.

... which is why when you divide 14 by 10 you do not get exactly
1.4, but something close to it like 1.3999999999999999111821580299875.
Since this is just a smidgen less than you expected (it might turn out
to be a smidgen greater, depending on the particular values involved),
your subsequent calculations are likely to be off by just a little.
As you've seen...

> tenDivs++;
> }
>
> i = 0;
> while (tenDivs>= 0) {
> while (!(foo< 1)) {

This is *so* much clearer than `while(foo >= 1)' ...

> foo--;
> oneDivs++;
> }
> str[i] = 48 + oneDivs;

"48?" Funny choice of "digits" you've made. But then, you
haven't shown us how oneDivs was declared and initialized, so maybe
I'm worrying about nothing.

> foo = foo * 10;
> oneDivs = 0;

Okay, so for places beyond the first you're using PQRSTUVWXY as
"decimal digits" on a system with ASCII-compatible encodings, or
(unassigned)(unassigned)(SYN)(unassigned)(PN)(RS)(UC)(EOT)(unassigned)
(unassigned) on an EBCDIC system, or Lord knows what on some other
system. I repeat: Funny choice of "digits."

> tenDivs--;
> i++;
> }
>
> Where "foo" is the value to be converted. Can anybody help, or point
> me to a source that can?
>
> In an unrelated question: Does anyone know of any link which describes
> the (relative) performance of all kinds of C operations? e.g: how fast is
> "add" comparing with "multiplication" on a typical machine.

Yes! All sorts of people will tell you all sorts of things
about what operations are cheap or costly. And I'll let you in on
a secret: 99.44% of those people are full of fertilizer, and the
other 0.56% who aren't will say "Fuhgeddaboudit." Like the days of
decimal floating-point, the days of "multiply is slower than add"
or "shift is faster than multiply" or "pointers are faster than
array indices" are long gone.

--
Eric Sosman
esosman@ieee-dot-org.invalid

Keith Thompson

6/8/2011 3:09:00 AM

0

Eric Sosman <esosman@ieee-dot-org.invalid> writes:
[...]
> This line is "behind your back." On nearly every computer these
> days, floating-point numbers use a base-two representation. A few
> use base-sixteen, but the days of base-ten floating-point are pretty
> much over and gone. (You'll still find it in hand-held calculators,
> but you'll be hard-pressed to find it anywhere else.)

Right [*].

[*] Here's the footnote. IBM has recently been
pushing decimal floating-point. See, for example,
<http://www.ibm.com/developerworks/wikis/display/WikiPtype/Decimal+Floating...
(which claims that The C draft standard includes _Decimal32,
_Decimal64, and _Decimal128, but N1570 has nothing like that).

I thought I remembered that the were storing 3 decimal digits (1000
values) in 10 bits (1024 values), which is almost as efficient
space-wise as pure binary, but I can't find a reference to that.

[...]

--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.ne...
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Peter Nilsson

6/8/2011 3:13:00 AM

0

Keith Thompson <ks...@mib.org> wrote:
> ... Floating-point numbers are represented in
> binary, not decimal.

Perhaps you where guessing the OP's implementation, but the
C language does not require that.

--
Peter

Keith Thompson

6/8/2011 3:27:00 AM

0

Peter Nilsson <airia@acay.com.au> writes:
> Keith Thompson <ks...@mib.org> wrote:
>> ... Floating-point numbers are represented in
>> binary, not decimal.
>
> Perhaps you where guessing the OP's implementation, but the
> C language does not require that.

You're right.

I don't *think* there are any existing C implementations that use
anything other than binary (or base 16, but that still represents the
significand in binary). IBM has decimal floating-point on some of its
systems, but not for the predefined types.

--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.ne...
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Keith Thompson

6/8/2011 3:30:00 AM

0

Thad Smith <ThadSmith@acm.org> writes:
> On 6/7/2011 2:10 PM, Dirk T. Shelley wrote:
>> I'm writing, for my own personal enlightenment, a little program
>> that will convert a floating-point number, passed as an argument on
>> the command line, into a character string. I'm now aware of
>> sprintf(), but I wasn't when I started writing this program, and I
>> feel it is my duty to finish it ;-)
>
> You have received at least three responses now with a combination of helpful
> comments and cheap shots. It's awfully easy to say "that's a strange way" (in a
> derogatory manner) to someone just learning what the language supports and not
> knowing the idioms developed over time.
>
> Ignore the pot shots. Pay attention to the posters that have the knowledge and
> patience to help others without making cutting remarks.

Maybe my news server has better filtering than yours. I haven't seen
anything in this thread that I'd consider snide. In particular,
I just checked and there are no other responses with the phrase
"that's a strange way".

--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.ne...
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Thad Smith

6/8/2011 3:39:00 AM

0

On 6/7/2011 2:10 PM, Dirk T. Shelley wrote:
> Hey all,
> I'm writing, for my own personal enlightenment, a little program
> that will convert a floating-point number, passed as an argument on
> the command line, into a character string. I'm now aware of
> sprintf(), but I wasn't when I started writing this program, and I
> feel it is my duty to finish it ;-)

Dirk,

You have received at least three responses now with a combination of helpful
comments and cheap shots. It's awfully easy to say "that's a strange way" (in a
derogatory manner) to someone just learning what the language supports and not
knowing the idioms developed over time.

Ignore the pot shots. Pay attention to the posters that have the knowledge and
patience to help others without making cutting remarks.


--
Thad

Eric Sosman

6/8/2011 3:39:00 AM

0

On 6/7/2011 11:08 PM, Keith Thompson wrote:
> Eric Sosman<esosman@ieee-dot-org.invalid> writes:
> [...]
>> This line is "behind your back." On nearly every computer these
>> days, floating-point numbers use a base-two representation. A few
>> use base-sixteen, but the days of base-ten floating-point are pretty
>> much over and gone. (You'll still find it in hand-held calculators,
>> but you'll be hard-pressed to find it anywhere else.)
>
> Right [*].
>
> [*] Here's the footnote. IBM has recently been
> pushing decimal floating-point. See, for example,
> <http://www.ibm.com/developerworks/wikis/display/WikiPtype/Decimal+Floating...
> (which claims that The C draft standard includes _Decimal32,
> _Decimal64, and _Decimal128, but N1570 has nothing like that).

Right [**].

[**] The last decimal F-P "computer" (as opposed to "calculator")
I used was an IBM system. In the Johnson administration.[***]

[***] Sorry, ambiguous: I mean the Johnson who became President
when his predecessor was assassinated.[****]

[****] Coincidence? YOU be the judge!

--
Eric Sosman
esosman@ieee-dot-org.invalid

Eric Sosman

6/8/2011 3:49:00 AM

0

On 6/7/2011 8:47 PM, Eric Sosman wrote:
> On 6/7/2011 5:10 PM, Dirk T. Shelley wrote:
>> [...]
>> str[i] = 48 + oneDivs;
>
> "48?" Funny choice of "digits" you've made.[...]

My mistake (and not my first, or last). I sort of thought
you probably meant '0' and double-checked by looking at an ASCII
table, and its formatting fooled me.

In any event, though, you should write '0' when you want the
integer that encodes the digit zero, not 48 or 240 or 30[*].

[*] Trivia question.

--
Eric Sosman
esosman@ieee-dot-org.invalid