I encountered a pesky error in Code::Blocks: the conversions functions (stod
, stof
, stoi
, ...) are not linked correctly.
I wanted to learn how to convert double or floats starting from a string. I started writing down a version of it, but I couldn't get it right even if I had understood the concept. Then I came upon this well explained algorithm.
The algorithm is in C style, so I tried to transform it into a C++ stylish format. I basically copied the algorithm and added a class for integer96 operations. Yet I would like to improve my C++ coding style, so I could use advices!
StringToDouble.h
This is composed of a prepNumber
, Parser
, converter
and stod
functions
prepNumber
is a structure used by Parser
and converter
.
Parser parses the string into a prepNumber
, converter
converts it to double
.
#ifndef STRINGTODOUBLE_H
#define STRINGTODOUBLE_H
#include <string>
#include <inttypes.h>
#include "int_96.h"
/*prepNumber: helper class storing the x * 10 ^ (y) number*/
class prepNumber{
public:
prepNumber();
bool negative; // negative flag for the negative
int32_t exponent; // store the exponent of the numnber v= 10^e * x e (-327:308)
uint64_t mantissa; // x in the above form
};
/*Parser: parses the string into prepNumber*/
class Parser{
public:
Parser();
prepNumber run(std::string init_string);
int get_machine_state();
int get_parser_state();
static int const PARSER_OK = 0;
// parser states...
private:
/*state machine states*/
static int const FSM_A = 0;
//...
int state;
int parser_state;
};
/*converter: converts the number into the double*/
double converter(prepNumber pn);
/*stod: wrapping function dealing with parse errors*/
double stod(std::string init_string);
#endif // STRINGTODOUBLE_H
StringToDouble.cpp
Constants and helpers:
#include "StringtoDouble.h"
/* Constants*/
#define DPOINT '.'
#define DIGITS 18 //significant digits of the number (mantissa)
#ifndef INT_MAX
#define INT_MAX 2147483647
#endif // INT_MAX
#define DOUBLE_PLUS_ZERO 0x0000000000000000ULL
#define DOUBLE_MINUS_ZERO 0x8000000000000000ULL
#define DOUBLE_PLUS_INFINITY 0x7FF0000000000000ULL
#define DOUBLE_MINUS_INFINITY 0xFFF0000000000000ULL
/* Helper functions */
// spaces are: 0x09 : 0x13 \t\n\r... 0x20= ' '
inline bool is_space(const char& x){
return (x >= 0x09 && x <= 0x13) || x == 0x20;
}
inline bool is_digit(const char& x){
return x >= '0' && x <= '9';
}
inline bool is_exp(const char& x){
return (x == 'e') || (x == 'E');
}
// scrolls the string pointer
inline char GETC(std::string::iterator& s){ return *s++; }
/* prepNumber */
prepNumber::prepNumber() : negative(false), exponent(0), mantissa(0) {}
Parser implementation
/* Parser */
Parser::Parser(){}
Parser::get_machine_state(){return state;}
Parser::get_parser_state() {return parser_state;}
prepNumber Parser::run(std::string init_string){
std::string::iterator s = init_string.begin();
parser_state = PARSER_OK; // reset the result
state = FSM_A; // set the state to A to enter the cycle
int c = ' '; // set c to ' ' to trigger getc.
int digx = 0;
bool expneg = false;
int32_t expexp = 0;
prepNumber pn;
while(state != FSM_STOP){
// state machine... see github or orginal code
}
if(expneg) {expexp = -expexp;}
pn.exponent += expexp;
// if mantissa is 0 or result < 10e-328 (double limit)
if(pn.mantissa == 0 || pn.exponent < -328){
parser_state = ((pn.negative)? PARSER_MZERO : PARSER_PZERO);
}
if(pn.exponent > 309){
parser_state = ((pn.negative)? PARSER_MINF : PARSER_PINF);
}
return pn;
}
Converter implementation
/* converter */
// helps conversion between uint64 and double
class HexDouble{
public:
HexDouble(){u = 0;}
void set(uint64_t n){u = n;}
double get(){return d;}
private:
union{
double d;
uint64_t u;
};
};
double converter(prepNumber pn){
int bin_exp = 92;
int_96 r;
int_96 s((uint64_t)pn.mantissa);
// multiply by 10 till significand get to the left most bit
while(pn.exponent > 0){
// multiplication by 10 (2^2 * 2^8) = (n<<1 + n<<3)
int_96 q = (s << 1) + (s << 3);
pn.exponent--;
//if the multiplication overflows in the last 4 of the 96 bits
// then increase the binary exponent
while(q.get_last_bits()){
q >>= 1;
bin_exp++;
}
}
// divide by 10 to get the significand to the left most bit
while(pn.exponent < 0){
while(!s.get_last_bit()){
s <<= 1;
bin_exp--;
}
// divide by 10
s.divide_by_10();
pn.exponent++;
}
// correct for the last 4 bits;
if(s != 0){
while(!s.get_last_bits()){
s<<=1;
bin_exp--;
}
}
// prepare the number <uint64 to double>
HexDouble hd;
//boundary checking
bin_exp += 1023; // addin bias
if(bin_exp > 2046){
hd.set((pn.negative)? DOUBLE_MINUS_INFINITY : DOUBLE_PLUS_INFINITY);
}
else if(bin_exp < 1){
hd.set((pn.negative)? DOUBLE_MINUS_ZERO : DOUBLE_PLUS_ZERO);
}
else if(s != 0){
uint64_t binexs2 = (uint64_t) bin_exp;
binexs2 <<= 52;
// construct complete mantissa
uint64_t signbit = (pn.negative)? (1ULL << 63) : 0;
uint64_t q = signbit | binexs2 | s.get_mantissa();
hd.set(q);
}
return hd.get();
}
I would appreciate any comment about style, clarity and exposition. I deleted some parts of the code in order to spare space and reading time yet the code is available at (https://github.com/Pella86/string_to_double)
int_96
the 4 most significant bits or the least? (s)(MMMm)bbb....bbbb(llll). I suspect it is the most significant. Then is thatsMMM
orMMMm
bits? Is the result signed? Maybe a ref toint_96.h
would help. \$\endgroup\$