Hexfloats

nick
Posts: 85
Joined: Mon Sep 08, 2014 9:24 pm
Location: Plymouth, UK

Hexfloats

Post 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).
nick
Posts: 85
Joined: Mon Sep 08, 2014 9:24 pm
Location: Plymouth, UK

Re: Hexfloats

Post 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.
nick
Posts: 85
Joined: Mon Sep 08, 2014 9:24 pm
Location: Plymouth, UK

Re: Hexfloats

Post 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"?
robn
Posts: 302
Joined: Mon Jul 01, 2013 1:11 am
Location: Melbourne, Australia

Re: Hexfloats

Post 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.
nick
Posts: 85
Joined: Mon Sep 08, 2014 9:24 pm
Location: Plymouth, UK

Re: Hexfloats

Post 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).
nick
Posts: 85
Joined: Mon Sep 08, 2014 9:24 pm
Location: Plymouth, UK

Re: Hexfloats

Post 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!
nick
Posts: 85
Joined: Mon Sep 08, 2014 9:24 pm
Location: Plymouth, UK

Re: Hexfloats

Post by nick »

robn - did your HexFloatToDouble function work? Are you using MSVC++ 2013?
FluffyFreak
Posts: 1343
Joined: Tue Jul 02, 2013 1:49 pm
Location: Beeston, Nottinghamshire, GB
Contact:

Re: Hexfloats

Post by FluffyFreak »

robn is on Linux of some variety :)

Only you and I are using MSVC2013.
impaktor
Posts: 1008
Joined: Fri Dec 20, 2013 9:54 am
Location: Tellus
Contact:

Re: Hexfloats

Post by impaktor »

FluffyFreak wrote:robn is on Linux of some variety :)
Vim + gcc I think
nick
Posts: 85
Joined: Mon Sep 08, 2014 9:24 pm
Location: Plymouth, UK

Re: Hexfloats

Post 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).
Post Reply