Re: converting floating point types round off error ....
On Oct 6, 2:38 pm, ma740988 <ma740...@gmail.com> wrote:
I'd like thank you all (James - as always) for clearing up my
confusion here. The claim was made that since the data is
being serialized I'll be subject to round off and truncation
errors above 6 digits (for single precision floating point
types). As Hans pointed out the 6 digits is a minimal
guarantee. As you all pointed out (and I'm paraphrasing) the
excess precision doesn't hurt. I have a follow- on question:
Now given this source.
# include <iostream>
class Serializer {
template <typename T>
void Swap( T& var ) {
char* start,*end;
char tmp;
start = (char * ) &var;
end = (char *)&var;
That's a reinterpret_cast. That should tell you immediately
that something is wrong.
end += sizeof(T)-1;
while(start < end) {
tmp = *start;
*start = *end;
*end = tmp;
start++;end--;
}
}
And the entire function looks very much like std::reverse< char* >,
called with a reintpret_cast, e.g.:
template< typename T >
void swap( T& var )
{
std::reverse( reintpret_cast< char* >( &var ),
reintpret_cast< char* >( &var + 1 ) ) ;
}
Any attempt to access the argument after having called this
function (unless T is a character type) is undefined behavior.
If T is an integral type, it will simply give an unspecified
value on most modern machines; if T is a floating point type,
there's a good chance of a core dump.
public :
//////////////////////////////////////////
/// @name Overloaded functions using "double"
//////////////////////////////////////////
char* put_data(char* out, const double& source) {
*(double *)out = source;
And this will core domp 7 times in 8 on my machine (Sun Sparc).
return out + sizeof(double);
}
char* get_data(double& target, char* source) {
target = *(double *)source;
As will this.
return source + sizeof(double);
}
char* put_swapped_data(char* out, const double& source) {
*(double *)out = source;
And this.
Swap(*(double *)out);
return out + sizeof(double);
}
char* get_swapped_data(double& target, char* source) {
target = *(double *)source;
And this.
Swap(target);
return source + sizeof(double);
}
//////////////////////////////////////////
/// @name Overloaded functions using "float"
//////////////////////////////////////////
char* put_data(char* out, const float& source) {
*(float *)out = source;
return out + sizeof(float);
}
char* get_data(float& target, char* source) {
target = *(float *)source;
return source + sizeof(float);
}
char* put_swapped_data(char* out, const float& source) {
*(float *)out = source;
Swap(*(float *)out);
return out + sizeof(float);
}
char* GetSwappedData(float& target, char* source) {
target = *(float *)source;
Swap(target);
return source + sizeof(float);
}
As above, except these will only core dump 3 times in 4, rather
than 7 in 8.
You can't take a char*, and assign a float or a double to it;
there's no guarantee that it is a legal address for a float or a
double.
///////////////////////////////////////////////
/// @name Overloaded functions using "unsigned char"
///////////////////////////////////////////////
char* put_data(char* out, const unsigned char& source) {
*(unsigned char *)out = source;
return out + sizeof(unsigned char);
}
char* get_data(unsigned char& target, char* source) {
target = *(unsigned char *)source;
return source + sizeof(unsigned char);
}
char* put_swapped_data(char* out, const unsigned char& source) {
*(unsigned char *)out = source;
return out + sizeof(unsigned char);
}
char* GetSwappedData(unsigned char& target, char* source) {
target = *(unsigned char *)source;
return source + sizeof(unsigned char);
}
///////////////////////////////////////////////
/// @name Overloaded functions using short*
///////////////////////////////////////////////
char* put_data(char* out, short* source,unsigned length16BitUnits)
{
memcpy(out, (char *)source, length16BitUnits*2);
return out + length16BitUnits*2;
}
char* get_data(short* target, char* source,unsigned
length16BitUnits) {
memcpy((char *)target,source,length16BitUnits*2);
return source+length16BitUnits*2;
}
char* put_swapped_data(char* out, short* source,unsigned
length16BitUnits) {
unsigned i;
char* tmp = put_data(out,source,length16BitUnits);
short* tmpShort = (short *)out;
for(i = 0; i < length16BitUnits; i++) {
Swap(*tmpShort);
tmpShort++;
}
return tmp;
}
char* GetSwappedData(short* target, char* source,unsigned
length16BitUnits) {
unsigned i;
char* tmp = get_data(target,source,length16BitUnits);
short* tmpShort=target;
for(i = 0; i < length16BitUnits; i++) {
Swap(*tmpShort);
tmpShort++;
}
return tmp;
}
};
int main () {
Serializer is ;
int const size_of_type = 40 ;
double source = 3.1415926535897932384626433832795;
char buffer [ size_of_type ] = { 0 };
char *ptr = is.put_data ( buffer, source ) ;
//std::cout << *ptr << std::endl;
std::cin.get() ;
}
First I think this ought to be written to use generic
programming. That aside (I'm not a fan of char* but the
vendor string facility - from what i understand is lacking),
how would I verify that the contents of source is in the
buffer and what is the required buffer size? (i.e based on the
function prototype - should the buffer size be size of type -
double in this case )
I'm not too sure what you're trying to do, but it looks like
you're playing funny games with types, which will get you into
trouble in the long run. (There are a few that you can
sometimes play, if you have to for performance reasons, but you
really have to know what you are doing.)
If the problem is just serialization, I'd go with your original
attempt to use textual formatting. It's a lot easier to debug,
for starters. For IEEE floating point, it is guaranteed that 9
decimal digits suffice for a round trip conversion for float,
and 17 for double (provided the conversion routines are
correct). See http://www.validlab.com/goldberg/paper.pdf, in
particular the section "Binary to Decimal Conversion".
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34