Eric Sosman
6/8/2011 12:47:00 AM
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