-
Notifications
You must be signed in to change notification settings - Fork 0
/
ldecimal.cpp
129 lines (127 loc) · 3.17 KB
/
ldecimal.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/******************************************************/
/* */
/* ldecimal.cpp - lossless decimal representation */
/* */
/******************************************************/
/* Copyright 2018,2023 Pierre Abbat.
* This file is part of the Quadlods program.
*
* The Quadlods program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Quadlods is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License and Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and Lesser General Public License along with Quadlods. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <cstdio>
#include <cfloat>
#include <cstring>
#include <cmath>
#include <cassert>
#include <clocale>
#include "ldecimal.h"
using namespace std;
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=setlocale(LC_NUMERIC,nullptr);
if (pLcNumeric)
saveLcNumeric=pLcNumeric;
setlocale(LC_NUMERIC,"C");
if (toler>0 && x!=0)
{
iexp=floor(log10(fabs(x/toler))-1);
if (iexp<0)
iexp=0;
}
else
iexp=DBL_DIG-1;
if (iexp>DBL_DIG)
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;
setlocale(LC_NUMERIC,saveLcNumeric.c_str());
return ret;
}