This is a follow up to https://codereview.stackexchange.com/questions/272333/conversion-from-long-double-to-string where I do the reverse conversion. This time around it is string to long double. It parses up to 38 dignificant digits, and I used a 128 significant bit floating point multiplication to convert decimal to binary, including the use of FE_TOWARDZERO mode to salvage the high significant bits from long double multiplications.
This code is designed for compilers with a long double of 64 significant bits (like Intel extended precision).
#include <stdint.h>
#include <fenv.h>
#include <string>
struct binary128tmp{
uint64_t m[2]; // 64 bits in each integer
short e;
};
struct int128tmp{
uint64_t q[2];
operator uint64_t(void){
return q[0];
}
operator long double(void){
return q[1]*0x1.p64l+q[0];
}
};
int128tmp toint128tmp(uint64_t a){
return {a, 0};
}
int128tmp operator*(int128tmp z, int128tmp x){
// rounding mode toward zero at this point
int128tmp w;
w.q[0]=z.q[0]*x.q[0];
w.q[1]=((long double)z.q[0]*x.q[0])*0x1.p-64l;
w.q[1]+=z.q[1]*x.q[0]+z.q[0]*x.q[1];
return w;
}
const uint64_t TENPOW19 = 10000000000000000000ULL;
int128tmp a64to128binmult(uint64_t a, uint64_t b){
int128tmp w;
w.q[0]=a*b;
w.q[1]=((long double)a*b)*0x1.p-64l;
return w;
}
binary128tmp powpow10p[] = {
{{0x0000000000000000,0xa000000000000000,}, 3,},
{{0x0000000000000000,0xc800000000000000,}, 6,},
{{0x0000000000000000,0x9c40000000000000,}, 13,},
{{0x0000000000000000,0xbebc200000000000,}, 26,},
{{0x0000000000000000,0x8e1bc9bf04000000,}, 53,},
{{0xf020000000000000,0x9dc5ada82b70b59d,}, 106,},
{{0x3cbf6b71c76b25fb,0xc2781f49ffcfa6d5,}, 212,},
{{0xc66f336c36b10137,0x93ba47c980e98cdf,}, 425,},
{{0xddbb901b98feeab8,0xaa7eebfb9df9de8d,}, 850,},
{{0xcc655c54bc5058fa,0xe319a0aea60e91c6,}, 1700,},
{{0x650d3d28f18b50d1,0xc976758681750c17,}, 3401,},
{{0xa74d28ce329ace57,0x9e8b3b5dc53d5de4,}, 6803,},
{{0xc94c153f804a4a9e,0xc46052028a20979a,},13606,},
};
binary128tmp powpow10m[] = {
{{0xcccccccccccccccd,0xcccccccccccccccc,},- 4,},
{{0x3d70a3d70a3d70a4,0xa3d70a3d70a3d70a,},- 7,},
{{0xd3c36113404ea4a9,0xd1b71758e219652b,},- 14,},
{{0xfdc20d2b36ba7c3e,0xabcc77118461cefc,},- 27,},
{{0x4c2ebe687989a9b6,0xe69594bec44de15b,},- 54,},
{{0x67de18eda5814af6,0xcfb11ead453994ba,},- 107,},
{{0x3f2398d747b3622b,0xa87fea27a539e9a5,},- 213,},
{{0xac7cb3f6d05ddbf1,0xddd0467c64bce4a0,},- 426,},
{{0xfa911155fefb5328,0xc0314325637a1939,},- 851,},
{{0x7132d332e3f20504,0x9049ee32db23d21c,},- 1701,},
{{0x87a601586bd3f703,0xa2a682a5da57c0bd,},- 3402,},
{{0x492512d4f2ead3d9,0xceae534f34362de4,},- 6804,},
{{0x2de38123a1c3d1af,0xa6dd04c8d2ce9fde,},-13607,},
};
binary128tmp operator*(binary128tmp N, binary128tmp M){
uint64_t w[4] = {0,0,0,0,};
int128tmp tmp = a64to128binmult(N.m[0],M.m[0]);
w[0]=tmp.q[0]; w[1]=tmp.q[1];
uint64_t z;
tmp = a64to128binmult(N.m[0],M.m[1]);
z = w[1]+tmp.q[0];
if(z<w[1]) {w[2]++;}
w[1]=z;
z = w[2]+tmp.q[1];
if(z<w[2]) {w[3]++;}
w[2]=z;
tmp = a64to128binmult(N.m[1],M.m[0]);
z = w[1]+tmp.q[0];
if(z<w[1]) {w[2]++;}
w[1]=z;
z = w[2]+tmp.q[1];
if(z<w[2]) {w[3]++;}
w[2]=z;
tmp = a64to128binmult(N.m[1],M.m[1]);
z = w[2]+tmp.q[0];
if(z<w[2]) {w[3]++;}
w[2]=z;
z = w[3]+tmp.q[1];
w[3]=z;
short exp = N.e+M.e;
if(w[3]<(1ULL<<63)){
w[3]<<=1;
for(int i=0; i<3; i++){
w[3-i]|=(w[2-i]>>63); w[2-i]<<=1;
}
}
else exp++;
if(w[1]>(1ULL<<63)||(w[1]==(1ULL<<63)&&(w[0]>0||(w[2]%2)))){
w[2]++; if(w[2]==0) {w[2]=0; w[3]++; if(w[3]==0){
w[3]=1ULL<<63; w[2]=0; exp++;
}}
}
binary128tmp out = {{w[2],w[3],},exp,};
return out;
}
long double stringtolongdouble(std::u16string num){
int i = 0;
bool sign = 0;
if(num.size()==0) return 0.l;
if(num[0]==u'-') {sign=1;i++;} if(num[0]==u'+') i++;
long double szero = sign ? -0.0l : 0.0l;
if(num.size()-i>=3&&(num[i+0]&0xFFDF)=='I'&&(num[i+1]&0xFFDF)=='N'&&(num[i+2]&0xFFDF)=='F')
return 1.0l/szero;
if(num.size()-i>=3&&(num[i+0]&0xFFDF)=='N'&&(num[i+1]&0xFFDF)=='A'&&(num[i+2]&0xFFDF)=='N')
return 0.0l/szero;
int rndmod = fegetround();
fesetround(FE_TOWARDZERO);
char digitregister[38];
for(int i=0; i<38; i++) digitregister[i]=0;
int r = 0;
int ex = -1;
while(i<num.size()&&(num[i]==u'0')) i++;
while(i<num.size()&&(num[i]>=u'0'&&num[i]<=u'9')&&r<38){
digitregister[r]=num[i]-u'0'; r++; i++; ex++;
}
if(i<num.size()&&num[i]==u'.'){
i++;
if(r==0) while(i<num.size()&&(num[i]==u'0')) {i++; ex--;}
while(i<num.size()&&(num[i]>=u'0'&&num[i]<=u'9')&&r<38){
digitregister[r]=num[i]-u'0'; r++; i++;
}
}
while(i<num.size()&&(num[i]>=u'0'&&num[i]<=u'9')) i++; //ignore over 38 digits
if(r==0) {fesetround(rndmod);return szero;}
if(i<num.size()&&(num[i+0]&0xFFDF)=='E'){
i++; bool sign2 = 0;
if(i<num.size()) {if(num[i+0]==u'-') {sign2=1;i++;} if(num[i+0]==u'+') i++;}
int ex2 = 0;
while(i<num.size()&&(num[i]>=u'0'&&num[i]<=u'9')) {ex2*=10; ex2+=num[i]-u'0'; i++;}
if(sign2) ex2=-ex2; ex+=ex2;
}
binary128tmp z;
{
uint64_t s = 0;
for(int j=0; j<19; j++) {s*=10; s+=digitregister[j];}
int128tmp r = a64to128binmult(s,TENPOW19);
s=0; for(int j=19; j<38; j++) {s*=10; s+=digitregister[j];}
if(r.q[0]+s<r.q[0])r.q[1]++; r.q[0]+=s;
z.e=127;
for(int e=2; e>=0; e--)
if(!(r.q[1]>>(64-(1<<e)))){r.q[1]<<=(1<<e);r.q[1]|=(r.q[0]>>(64-(1<<e)));r.q[0]<<=(1<<e); z.e-=(1<<e);}
z.m[0]=r.q[0]; z.m[1]=r.q[1];
}
binary128tmp x = {{0,1ULL<<63,},0,};
ex-=37;
if(ex>0) for(int i=13; i>=0; i--){if(((+ex)>>i)&1)x=x*powpow10p[i];}
if(ex<0) for(int i=13; i>=0; i--){if(((-ex)>>i)&1)x=x*powpow10m[i];}
x=x*z;
fesetround(rndmod);
if(x.e<-16446) return szero;
if(x.e==-16446){if(x.m[0]&&!(x.m[1]&1)) x.m[1]++; x.m[0]=x.m[1]; x.m[1]=0;}
if(x.e<-16382&&x.e>-16446) {
uint64_t y = -16382-x.e;
bool flag = 0;
if((x.m[0]&((1ULL<<y)-1))&&!((x.m[0]>>y)&1)) flag = 1;
x.m[0]>>=y; x.m[0]|=x.m[1]<<(64-y); x.m[1]>>=y;
if(flag) {uint64_t p = x.m[0]+1; if(p<x.m[0])x.m[1]++; x.m[0]=p;}
}
if(x.m[0]>(1ULL<<63)||(x.m[0]==(1ULL<<63)&&(x.m[1]&1))) {x.m[1]++; if(x.m[1]==0) {x.m[1]=1ULL<63; x.e++;}}
if(x.e<-16382) x.e=-16383;
if(x.e>16383) return 1.0l/szero;
long double out;
*(uint64_t*)&out = x.m[1];
*((short*)&out+4) = (x.e+16383)|((int)sign<<15);
return out;
}
#include <iostream>
#include <iomanip>
#include <math.h>
int main(){
std::u16string a = u"4e-4951";
long double b = stringtolongdouble(a);
std::cout << b;
}
<math.h>and not<cmath>? \$\endgroup\$