Printing double value in a fixed size buffer

M

mathieu

Hi there,

I am trying to find a solution for the following problem: how do I
print in ASCII a double value in a fixed size buffer of 16 bytes ?

My first attempt was:

double data = 0.960000000000662;
std::cout << std::dec << std::setprecision(15) << data << std::endl;

Because it does not work for number matching "0.[0]*", I was
suggested:

std::cout << std::dec << std::setprecision(15) << std::fixed << data
<< std::endl;

This still does not work for:

double data = 1000000.960000000000662;

and std::fixed is actually loosing all the precision for number such
as:

double data = 0.0000000000009654321;

Could someone please let me know how I can use the iomanip functions
to solve my issue ? Requirements are:
- Up to a maximum of 16 bytes
- Only [0-9], 'e'/'E', '+'/'-' and '.' character can be used
- Minimize loss of information/precision

Thanks for your time,
-Mathieu
 
M

mathieu

mathieu said:
I am trying to find a solution for the following problem: how do I
print in ASCII a double value in a fixed size buffer of 16 bytes ?
My first attempt was:
double data = 0.960000000000662;
std::cout << std::dec << std::setprecision(15) << data << std::endl;
Because it does not work for number matching "0.[0]*", I was
suggested:
std::cout << std::dec << std::setprecision(15) << std::fixed << data
<< std::endl;
This still does not work for:
double data = 1000000.960000000000662;

If your 'double' is like most of ours, then you have only 16 significant
digits, while here you typed 22! What you put in your program

double data = 1000000.960000000000662;
// 1234567 890123456 <--

is just a *string*. The conversion your compiler performs throws your
'00662' away anyway, most likely. The value in the running program does
*not* contain any information beyond

double data = 1000000.960000000;

for it to output. Of course it "doesn't work". You need an arbitrary
precision library to be able to use the 22 digits. Or you could try to
define the number 'long double', but there is no guarantee of the better
precision; for all we know the compiler implements 'long double' and
'double' exactly the same way (as it's allowed to).

Hum, my gcc version must be doing some kind of optimization (maybe
because of the const qualifier) because almost all digits were being
printed on screen.
and std::fixed is actually loosing all the precision for number such
as:
double data = 0.0000000000009654321;

Because 'fixed' is designed to actually consider the leading zeros as
significant. You seem to have conflicting requirements.


Could someone please let me know how I can use the iomanip functions
to solve my issue ? Requirements are:
- Up to a maximum of 16 bytes
- Only [0-9], 'e'/'E', '+'/'-' and '.' character can be used
- Minimize loss of information/precision

Try 'scientific' manipulator (the analog is the '%g' format in C).

I guess this should work, scientific might be the general solution
although it 'eats up' 4digits just for the exponent even when it is 0.

Thanks,
 
M

mojmir

Hum, my gcc version must be doing some kind of optimization (maybe
because of the const qualifier) because almost all digits were being
printed on screen.

i think this is related to this (citing from gcc-help)

"If it's x86 then this is the
classic issue of excess precision, wherein operations within the 387
unit occur in extended (80 bit) precision but when transferred out of
the FP unit and stored to memory are truncated to double precision (64
bit.) If you compare a value that has just been computed with one
that
has been previously computed and then stored to memory, they won't
necessarily be equal since one has been truncated while the other
still
has its excess precision. But by passing it on the stack to cout you
essentially force a memory store which discards the excess precision
so
they both appear the same."

hope it helps and relates to topic,
mojmir
 
J

James Kanze

I am trying to find a solution for the following problem: how
do I print in ASCII a double value in a fixed size buffer of
16 bytes ?
My first attempt was:
double data = 0.960000000000662;
std::cout << std::dec << std::setprecision(15) << data << std::endl;
Because it does not work for number matching "0.[0]*", I was
suggested:
std::cout << std::dec << std::setprecision(15) << std::fixed << data
<< std::endl;
This still does not work for:
double data = 1000000.960000000000662;
and std::fixed is actually loosing all the precision for
number such as:
double data = 0.0000000000009654321;
Could someone please let me know how I can use the iomanip functions
to solve my issue ? Requirements are:
- Up to a maximum of 16 bytes
- Only [0-9], 'e'/'E', '+'/'-' and '.' character can be used
- Minimize loss of information/precision

It depends on what you really want and need, but if the values
can occupy a large range, you need to use scientific format (and
even then, you have to watch out for values greater than 1E100
between 0 and 1E-100). Of course, this means that you won't get
15 significant digits in a 16 byte buffer; you have to allow for
the sign, the decimal point, and the exponant part (4
characters). So you end up with something like:
std::cout << EFmt( 16, 9 ) << value ;
with ten digits precision (9 after the decimal point). If you
need more precision over a large range, you need more
characters. (EFmt( 16, 9 ) will generate something matching the
regular expression "[ -][0-9][.,][0-9]{9}e[+-][0-9]{2-}". If
the exponent is less than 100 and greater than -100, that last
{} becomes {2}, and you have a constant width. And whether . or
, is used for the third character depends on the locale. (My
own implementation of EFmt allows adding flags, e.g.
std::ios::uppercase for E instead of e, std::ios::showsign for
[+-] instead of the first [ -], etc. Typically, however, you'll
create a manipulator specifically for the context you're
interested in, with a name designating that context, and use
it.)
 
J

James Kanze

[...]
Could someone please let me know how I can use the iomanip functions
to solve my issue ? Requirements are:
- Up to a maximum of 16 bytes
- Only [0-9], 'e'/'E', '+'/'-' and '.' character can be used
- Minimize loss of information/precision
Try 'scientific' manipulator (the analog is the '%g' format in C).

'scientific' is the equivalent of '%e', not '%g'. The default
is the equivalent of '%g', and the only way to get it is:

std::cout.setf( std::ios::fmtflags(), std::ios::floatfield ) ;

There's no manipulator (but you can easily write your own, of
course---just as you'd do for the other formats.
 

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

Forum statistics

Threads
473,968
Messages
2,570,152
Members
46,697
Latest member
AugustNabo

Latest Threads

Top