PierreA
13th September 2017, 17:15
I have the following function:
string ldecimal(double x,double toler)
{
double x2;
int h,i,iexp,chexp;
size_t zpos;
char *dotpos,*epos,*pLcNumeric;
string ret,s,m,antissa,exponent,saveLcNumeric;
char buffer[32],fmt[8];
assert(toler>=0);
pLcNumeric=getenv("LC_NUMERIC");
if (pLcNumeric)
saveLcNumeric=pLcNumeric;
setenv("LC_NUMERIC","C",true);
if (toler>0 && x!=0)
{
iexp=floor(log10(fabs(x/toler))-1);
if (iexp<0)
iexp=0;
}
else
iexp=DBL_DIG-1;
h=-1;
i=iexp;
while (true)
{
sprintf(fmt,"%%.%de",i);
sprintf(buffer,fmt,x);
x2=atof(buffer);
if (h>0 && (fabs(x-x2)<=toler || i>=DBL_DIG+3))
break;
if (fabs(x-x2)>toler || i<=0)
h=1;
i+=h;
}
dotpos=strchr(buffer,'.');
epos=strchr(buffer,'e');
if (epos && !dotpos) // e.g. 2e+00 becomes 2.e+00
{
memmove(epos+1,epos,buffer+31-epos);
dotpos=epos++;
*dotpos=='.';
}
if (dotpos && epos)
{
m=string(buffer,dotpos-buffer);
antissa=string(dotpos+1,epos-dotpos-1);
exponent=string(epos+1);
if (m.length()>1)
{
s=m.substr(0,1);
m.erase(0,1);
}
iexp=atoi(exponent.c_str());
zpos=antissa.find_last_not_of('0');
antissa.erase(zpos+1);
iexp=stoi(exponent);
if (iexp<0 && iexp>-5)
{
antissa=m+antissa;
m="";
iexp++;
}
if (iexp>0)
{
chexp=iexp;
if (chexp>antissa.length())
chexp=antissa.length();
m+=antissa.substr(0,chexp);
antissa.erase(0,chexp);
iexp-=chexp;
}
while (iexp>-5 && iexp<0 && m.length()==0)
{
antissa="0"+antissa;
iexp++;
}
while (iexp<3 && iexp>0 && antissa.length()==0)
{
m+='0';
iexp--;
}
sprintf(buffer,"%d",iexp);
exponent=buffer;
ret=s+m;
if (antissa.length())
ret+='.'+antissa;
if (iexp)
ret+='e'+exponent;
}
else
ret=buffer;
setenv("LC_NUMERIC",saveLcNumeric.c_str(),true);
return ret;
}
The function is used to format numbers when writing to files, so it has to use '.' for the decimal point regardless of locale. I added the saving and restoring LC_NUMERIC code to fix this bug. It didn't work.
I am using it in at least two programs: bezitest, which is a non-Qt test program, and viewtin, which is a Qt GUI program. Until I implement hit-testing, viewtin is displaying the coordinates of the cursor in world space. "bezitest ldecimal" outputs, among others, "3.141592653589793". viewtin displays a tooltip saying something like "3,141592653589793,2,718281828459045". I ran it in gdb and found that the comma is put there by sprintf.
locale outputs the following:
LANG=en_DK.UTF-8
LANGUAGE=en_US:es
LC_CTYPE="en_DK.UTF-8"
LC_NUMERIC="en_DK.UTF-8"
LC_TIME="en_DK.UTF-8"
LC_COLLATE="en_DK.UTF-8"
LC_MONETARY="en_DK.UTF-8"
LC_MESSAGES="en_DK.UTF-8"
LC_PAPER="en_DK.UTF-8"
LC_NAME="en_DK.UTF-8"
LC_ADDRESS="en_DK.UTF-8"
LC_TELEPHONE="en_DK.UTF-8"
LC_MEASUREMENT="en_DK.UTF-8"
LC_IDENTIFICATION="en_DK.UTF-8"
LC_ALL=
LC_NUMERIC is actually unset.
string ldecimal(double x,double toler)
{
double x2;
int h,i,iexp,chexp;
size_t zpos;
char *dotpos,*epos,*pLcNumeric;
string ret,s,m,antissa,exponent,saveLcNumeric;
char buffer[32],fmt[8];
assert(toler>=0);
pLcNumeric=getenv("LC_NUMERIC");
if (pLcNumeric)
saveLcNumeric=pLcNumeric;
setenv("LC_NUMERIC","C",true);
if (toler>0 && x!=0)
{
iexp=floor(log10(fabs(x/toler))-1);
if (iexp<0)
iexp=0;
}
else
iexp=DBL_DIG-1;
h=-1;
i=iexp;
while (true)
{
sprintf(fmt,"%%.%de",i);
sprintf(buffer,fmt,x);
x2=atof(buffer);
if (h>0 && (fabs(x-x2)<=toler || i>=DBL_DIG+3))
break;
if (fabs(x-x2)>toler || i<=0)
h=1;
i+=h;
}
dotpos=strchr(buffer,'.');
epos=strchr(buffer,'e');
if (epos && !dotpos) // e.g. 2e+00 becomes 2.e+00
{
memmove(epos+1,epos,buffer+31-epos);
dotpos=epos++;
*dotpos=='.';
}
if (dotpos && epos)
{
m=string(buffer,dotpos-buffer);
antissa=string(dotpos+1,epos-dotpos-1);
exponent=string(epos+1);
if (m.length()>1)
{
s=m.substr(0,1);
m.erase(0,1);
}
iexp=atoi(exponent.c_str());
zpos=antissa.find_last_not_of('0');
antissa.erase(zpos+1);
iexp=stoi(exponent);
if (iexp<0 && iexp>-5)
{
antissa=m+antissa;
m="";
iexp++;
}
if (iexp>0)
{
chexp=iexp;
if (chexp>antissa.length())
chexp=antissa.length();
m+=antissa.substr(0,chexp);
antissa.erase(0,chexp);
iexp-=chexp;
}
while (iexp>-5 && iexp<0 && m.length()==0)
{
antissa="0"+antissa;
iexp++;
}
while (iexp<3 && iexp>0 && antissa.length()==0)
{
m+='0';
iexp--;
}
sprintf(buffer,"%d",iexp);
exponent=buffer;
ret=s+m;
if (antissa.length())
ret+='.'+antissa;
if (iexp)
ret+='e'+exponent;
}
else
ret=buffer;
setenv("LC_NUMERIC",saveLcNumeric.c_str(),true);
return ret;
}
The function is used to format numbers when writing to files, so it has to use '.' for the decimal point regardless of locale. I added the saving and restoring LC_NUMERIC code to fix this bug. It didn't work.
I am using it in at least two programs: bezitest, which is a non-Qt test program, and viewtin, which is a Qt GUI program. Until I implement hit-testing, viewtin is displaying the coordinates of the cursor in world space. "bezitest ldecimal" outputs, among others, "3.141592653589793". viewtin displays a tooltip saying something like "3,141592653589793,2,718281828459045". I ran it in gdb and found that the comma is put there by sprintf.
locale outputs the following:
LANG=en_DK.UTF-8
LANGUAGE=en_US:es
LC_CTYPE="en_DK.UTF-8"
LC_NUMERIC="en_DK.UTF-8"
LC_TIME="en_DK.UTF-8"
LC_COLLATE="en_DK.UTF-8"
LC_MONETARY="en_DK.UTF-8"
LC_MESSAGES="en_DK.UTF-8"
LC_PAPER="en_DK.UTF-8"
LC_NAME="en_DK.UTF-8"
LC_ADDRESS="en_DK.UTF-8"
LC_TELEPHONE="en_DK.UTF-8"
LC_MEASUREMENT="en_DK.UTF-8"
LC_IDENTIFICATION="en_DK.UTF-8"
LC_ALL=
LC_NUMERIC is actually unset.