James Kanze a écrit :
itdevries a écrit :
I'm trying to convert some char data I read from a binary
file (using ifstream) to a float type. I've managed to
convert the int types but now I need to do the float types
as well but it doesn't seem to work. The code below is what
I'm trying to use. Anyone see any obvious errors? or have
any hints/pointers?
I suppose charBuf is of the type char[] ? In this case
'charBuf[ii] << offset' is 0 as soon as offset>=8 so you get
only 0s.
Since when? That's not what my compiler does, and it's not
what the standard requires. In expressions, char's are
promoted to int's.
Then, it is my mistake and I am the worse for not checking. I
am always careful to avoid writing code like that.
What's wrong with
memcpy(&floatRead,charBuf+startInd,sizeof(floatRead));
?
Maybe the fact that it doesn't have the correct semantics? (It
might work, sometimes, but it's certainly a risky procedure.
More risky than using bitwise operators to build a float ?
Yes.
Basically, using the bitwise operators depends on the external
representation being the same as the internal, i.e. they are
both IEEE (or both something else, but I've never seen that
case). In practice, a lot of modern protocols do use IEEE, and
most modern small and medium sized computers also use it
internally, so if you are only targetting Windows and mainstream
Unix, you're safe (and a few judicious #if on the values defined
in <climits>, controlling an #error, can assure that it probably
won't compile on systems which don't use IEEE).
It's ugly, but the alternative is a lot more complex.
(Interestingly, it's not that much slower on my machine. Which
rather surprised me.)
Concerning the semantic, I don't see what could be a correct
one in this case since reading a binary flow to build a float
is however heretical; unless you have some kind of standard
transform and assuming the capacities of representation are
equivalent across addressed architectures.
As mentionned above, a lot of protocols (BER being the only
exception which comes to mind) use IEEE format for floating
point, as do a lot of machines. In this case, reading the value
as if it were the same sized unsigned integer type (uint32_t or
uint64_t, depending on whether you're reading float or double),
followed by some sort of nasty cast, will work.
What is uncorrect with dumping the memory area representing a
POD and reading it back in the same program ?
The fact that there are still significant differences in the
binary representation, particularly with regards to byte order.
If you want to be really correctly, you need something like:
uint32_t tmp ;
operator>>( tmp ) ;
if ( *this ) {
float f = 0.0 ;
if ( (tmp & 0x7FFFFFFF) != 0 ) {
f = ldexp( ((tmp & 0x007FFFFF) | 0x00800000),
(int)((tmp & 0x7F800000) >> 23) - 126 - 24 ) ;
}
if ( (tmp & 0x80000000) != 0 ) {
f = -f ;
}
dest = f ;
}
return *this ;
You still read the type into the unsigned int (using shifts),
but then you extract the fields as they are specified in the
format, and use them to create a float, using standard functions
to exploit their values in a portable fashion. As I've said
previously, this is probably more work than necessary, and until
needed, I'd just go with the ugly cast (and the #if/#error, to
ensure that it doesn't get used when not appropriate).
In Boost 1.35 they've added an optimization to take advantage of
contiguous collections of primitive data types. Here is a copy
of a file that is involved:
#ifndef BOOST_ARCHIVE_BASIC_BINARY_OPRIMITIVE_HPP
#define BOOST_ARCHIVE_BASIC_BINARY_OPRIMITIVE_HPP
// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
/////////
1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// basic_binary_oprimitive.hpp
// (C) Copyright 2002 Robert Ramey -
http://www.rrsd.com .
// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or
copy at
//
http://www.boost.org/LICENSE_1_0.txt)
// See
http://www.boost.org for updates, documentation, and revision
history.
// archives stored as native binary - this should be the fastest way
// to archive the state of a group of obects. It makes no attempt to
// convert to any canonical form.
// IN GENERAL, ARCHIVES CREATED WITH THIS CLASS WILL NOT BE READABLE
// ON PLATFORM APART FROM THE ONE THEY ARE CREATE ON
#include <iosfwd>
#include <cassert>
#include <locale>
#include <cstddef> // size_t
#include <streambuf> // basic_streambuf
#include <string>
#include <boost/config.hpp>
#if defined(BOOST_NO_STDC_NAMESPACE)
namespace std{
using ::size_t;
} // namespace std
#endif
#include <boost/cstdint.hpp>
//#include <boost/limits.hpp>
//#include <boost/io/ios_state.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/throw_exception.hpp>
#include <boost/archive/basic_streambuf_locale_saver.hpp>
#include <boost/archive/archive_exception.hpp>
#include <boost/archive/detail/auto_link_archive.hpp>
#include <boost/serialization/is_bitwise_serializable.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/serialization/array.hpp>
#include <boost/archive/detail/abi_prefix.hpp> // must be the last
header
namespace boost {
namespace archive {
/////////////////////////////////////////////////////////////////////////
// class basic_binary_oprimitive - binary output of prmitives
template<class Archive, class Elem, class Tr>
class basic_binary_oprimitive
{
#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
friend class save_access;
protected:
#else
public:
#endif
std::basic_streambuf<Elem, Tr> & m_sb;
// return a pointer to the most derived class
Archive * This(){
return static_cast<Archive *>(this);
}
boost::scoped_ptr<std::locale> archive_locale;
basic_streambuf_locale_saver<Elem, Tr> locale_saver;
// default saving of primitives.
template<class T>
void save(const T & t)
{
save_binary(& t, sizeof(T));
}
/////////////////////////////////////////////////////////
// fundamental types that need special treatment
// trap usage of invalid uninitialized boolean which would
// otherwise crash on load.
void save(const bool t){
int i = t;
assert(0 == i || 1 == i);
save_binary(& t, sizeof(t));
}
BOOST_ARCHIVE_OR_WARCHIVE_DECL(void)
save(const std::string &s);
#ifndef BOOST_NO_STD_WSTRING
BOOST_ARCHIVE_OR_WARCHIVE_DECL(void)
save(const std::wstring &ws);
#endif
BOOST_ARCHIVE_OR_WARCHIVE_DECL(void)
save(const char * t);
BOOST_ARCHIVE_OR_WARCHIVE_DECL(void)
save(const wchar_t * t);
BOOST_ARCHIVE_OR_WARCHIVE_DECL(void)
init();
BOOST_ARCHIVE_OR_WARCHIVE_DECL(BOOST_PP_EMPTY())
basic_binary_oprimitive(
std::basic_streambuf<Elem, Tr> & sb,
bool no_codecvt
);
BOOST_ARCHIVE_OR_WARCHIVE_DECL(BOOST_PP_EMPTY())
~basic_binary_oprimitive();
public:
// we provide an optimized save for all fundamental types
// typedef serialization::is_bitwise_serializable<mpl::_1>
// use_array_optimization;
// workaround without using mpl lambdas
struct use_array_optimization {
template <class T>
struct apply : public serialization::is_bitwise_serializable<T>
{};
};
// the optimized save_array dispatches to save_binary
template <class ValueType>
void save_array(serialization::array<ValueType> const& a, unsigned
int)
{
save_binary(a.address(),a.count()*sizeof(ValueType));
}
void save_binary(const void *address, std::size_t count);
};
template<class Archive, class Elem, class Tr>
inline void
basic_binary_oprimitive<Archive, Elem, Tr>::save_binary(
const void *address,
std::size_t count
){
//assert(
//
static_cast<std::size_t>((std::numeric_limits<std::streamsize>::max)
()) >= count
//);
// note: if the following assertions fail
// a likely cause is that the output stream is set to "text"
// mode where by cr characters recieve special treatment.
// be sure that the output stream is opened with ios::binary
//if(os.fail())
//
boost::throw_exception(archive_exception(archive_exception::stream_error));
// figure number of elements to output - round up
count = ( count + sizeof(Elem) - 1)
/ sizeof(Elem);
std::streamsize scount = m_sb.sputn(
static_cast<const Elem *>(address),
count
);
if(count != static_cast<std::size_t>(scount))
boost::throw_exception(archive_exception(archive_exception::stream_error));
//os.write(
// static_cast<const BOOST_DEDUCED_TYPENAME OStream::char_type
*>(address),
// count
//);
//assert(os.good());
}
} //namespace boost
} //namespace archive
#include <boost/archive/detail/abi_suffix.hpp> // pop pragams
#endif // BOOST_ARCHIVE_BASIC_BINARY_OPRIMITIVE_HPP
I guess it boils down to a call of basic_streambuf<>::sputn(). Is
that any
better IYO than memcpy?
Brian Wood
Ebenezer Enterprises
www.webEbenezer.net