Page 1 of 2

Hexfloats

Posted: Sun Jan 25, 2015 7:16 pm
by nick
Saving floats and doubles as hex strings (e.g. 0x1.7bb06fp+15) and parsing back into float/double is not as easy as I thought.
The following functions create a hex float/double string:

Code: Select all

std::string HexFloat(float val)
{
	char hex[128]; // Probably don't need such a large char array.
	std::sprintf(hex, "%a", val);
	return hex;
}
std::string HexDouble(double val)
{
	char hex[128]; // Probably don't need such a large char array.
	std::sprintf(hex, "%a", val);
	return hex;
}
But the corresponding parsing functions are giving nonsense (0.0000 or some outrageous number):

Code: Select all

float HexFloat(std::string str)
{
	float val;
	std::sscanf(str.c_str(), "%a", &val);
	return val;
}
double HexDouble(std::string str)
{
	double val;
	std::sscanf(str.c_str(), "%a", &val);
	return val;
}
I've also tried:

Code: Select all

float HexFloat(std::string str)
{
	return std::strtof(str.c_str(), 0);
}
double HexDouble(std::string str)
{
	return std::strtod(str.c_str(), 0);
}
Note I'm using the "std" versions of sprintf and sscanf (because the MSVC default version of sscanf doesn't allow for the "%a" type field).
Even if I get this to work, there's no guarantee that it'll work for other platforms/compilers.
There's another method talked about, that uses memcpy into an int.
std::hexfloat seems to be something to do with streams, so I can't see how to use it here.
Any suggestions will be gratefully received :)
[Edit] This might have to be coded from first principles (and method will be dependent on how float/double is stored in bits).

Re: Hexfloats

Posted: Sun Jan 25, 2015 7:23 pm
by nick
The purpose of a hexfloat is to preserve precision of a floating point value when storing it as ascii chars. How essential is it that we preserve precision? Would certain objects appear to "drift" in space following each successive save/load?
Unfortunately there appears to be very little useful information out there regarding parsing the hexfloat.

Re: Hexfloats

Posted: Sun Jan 25, 2015 7:53 pm
by nick
The documentation for std::strtof and std::strtod
http://en.cppreference.com/w/cpp/string/byte/strtof
says that the string can be a binary floating point expression, consisting of the following parts:

* (optional) plus or minus sign
* 0x or 0X
* nonempty sequence of hexadecimal digits optionally containing a decimal point character (as determined by the current C locale) (defines significand)
* (optional) p or P followed with optional minus or plus sign and nonempty sequence of decimal digits (defines exponent)

so it *should* work.
Do you think it's something to do with "locale"?

Re: Hexfloats

Posted: Sun Jan 25, 2015 10:11 pm
by robn
I did pretty much the same thing:

Code: Select all

// helper methods for safely storing/retrieving doubles
inline char *DoubleToHexFloat(double v) {
       static char buf[32];
       snprintf(buf, sizeof(buf), "%a", v);
       return buf;
}
inline double HexFloatToDouble(const char *buf) {
       double v;
       sscanf(buf, "%lf", &v);
       return v;
}
inline double HexFloatToDouble(const std::string &str) {
       return HexFloatToDouble(str.c_str());
}
scanf's 'a' conversion should be equivalent to 'f'. printf's 'a' conversion is definitely for doubles though, thus my use of %lf over a straight %f.

Re: Hexfloats

Posted: Sun Jan 25, 2015 11:08 pm
by nick
So %f, %e, %g and %a are fine for both floats and doubles in printf, but in scanf we need:
%f, %e, %g and %a for floats
%lf, %le, %lg and %la for doubles

I'll have a go using %la in scanf for double.
I've been examining serialisation/unserialisation of a double (Game::m_time) rather than a float (the code might be working for floats - just not doubles).

Re: Hexfloats

Posted: Sun Jan 25, 2015 11:43 pm
by nick
This doesn't look good:
http://stackoverflow.com/questions/2390 ... -returns-0
It could be that sscanf isn't supporting C99.

[Edit] Definately not looking good: http://blogs.msdn.com/b/vcblog/archive/ ... ageIndex=5
A search for "hexfloat" reveals a comment:
"I've filed DevDiv#744423 "<stdio.h>: sscanf() can't parse hexfloat" and assigned it to our CRT maintainer. We won't be able to fix this in 2013 RTM, but we'll look at it for the next major release. (The printf/scanf family's format specifiers need to be updated for C99, there are other issues in this area.)"
So I might have to find another way around this.

For now, I'll write HexFloat/HexDouble to convert to/from standard decimal & exponent fomat. HexFloat/HexDouble can be updated to use hex format later.

I was hoping there would be a standard function to do this. I expect I can write code to extract and reassemble signs, mantissas and exponents of floats and doubles, but it'll take a while, and it might be platform/compiler specific!

Re: Hexfloats

Posted: Mon Jan 26, 2015 1:29 am
by nick
robn - did your HexFloatToDouble function work? Are you using MSVC++ 2013?

Re: Hexfloats

Posted: Mon Jan 26, 2015 10:41 am
by FluffyFreak
robn is on Linux of some variety :)

Only you and I are using MSVC2013.

Re: Hexfloats

Posted: Mon Jan 26, 2015 10:51 am
by impaktor
FluffyFreak wrote:robn is on Linux of some variety :)
Vim + gcc I think

Re: Hexfloats

Posted: Mon Jan 26, 2015 11:57 pm
by nick
Exact representation of a float/double can be achieved going via an integer of the corresponding size (32 or 64 bit).
However, the printed number in the text file will not be human readable (will be some nonsense integer value) and probably not platform independent (e.g. game saved on linux won't load in windows) due to different floating point bit patterns. A simple convertor program could be used (on a particular platform) to translate float/double <--> integer (useful if someone say wanted to manually adjust a double in a save file).

* memcpy the float/double into an int of the corresponding size (32 or 64 bit).
* Save the int to an ascii string using sprintf.
* When loading, parse as normal using sscanf into an int 32/64 bit.
* memcpy to a float/double.

This method is used in the existing serialisation (except that the int is a char array and saved as binary).