1 // format_float_to_string().
2 
3 // General includes.
4 #include "base/cl_sysdep.h"
5 
6 // Specification.
7 #include "real/format-output/cl_format.h"
8 
9 
10 // Implementation.
11 
12 // BUGS:
13 // - This is slow.
14 
15 #include "cln/output.h"
16 #include "cln/malloc.h"
17 #include "cln/float.h"
18 #include "cln/integer.h"
19 #include "integer/cl_I.h"
20 #include "base/string/cl_spushstring.h"
21 
22 namespace cln {
23 
24 // format_float_to_string(arg,width,d,k,dmin)
25 // ergibt einen String zum Floating-point arg:
26 // er hat den Wert von abs(arg)*expt(10,k), dabei mind. d Nachkommastellen
27 // und höchstens die Länge width (width<=0 -> keine Einschränkung).
28 // Trotzdem wird nicht auf weniger als dmin Stellen gerundet.
29 
format_float_to_string(const cl_F & arg,const sintL width,const sintL d,const sintL k,const sintL dmin)30 const digits_with_dot format_float_to_string (const cl_F& arg, const sintL width, const sintL d, const sintL k, const sintL dmin)
31 {
32 	// One pre-allocated buffer. This reduces the allocation/free cost.
33 	static cl_spushstring digitstring;
34 
35 	if (zerop(arg)) {
36 		var sintL places = (d < dmin ? dmin : d);
37 		if (width > 0)
38 			// width angegeben -> places := min(places,width-1)
39 			if (places >= width)
40 				places = width-1;
41 		// ein Punkt und places Nullen
42 		var char* string = (char *) malloc_hook(1+places+1);
43 		string[0] = '.';
44 		for (sintL i = 1; i <= places; i++) string[i] = '0';
45 		string[1+places] = '\0';
46 		return digits_with_dot(string, 1+places,
47 				true, (places==0), 0
48 			);
49 	}
50 	// significand : Integer >0
51 	// expon : Integer
52 	// mantprec : Anzahl der echten Mantissenbits von significand
53 	// (also 2^mantprec <= significand < 2^(mantprec+1))
54 	// width : Anzahl Stellen, die die Zahl (inklusive Punkt) nicht
55 	//         überschreiten soll, oder 0
56 	// d : Mindestanzahl Nachkommastellen oder 0
57 	// k : Skalierungsfaktor (siehe CLTL S.394)
58 	// dmin : Mindestanzahl von Dezimaltellen, die (trotz Angabe von width
59 	//        oder d) nicht gerundet werden dürfen.
60 	//        (Nur interessant, falls d <= dmin <= (precision der Zahl).)
61 	// wandelt die Zahl significand*2^expon um in einen Dezimalstring um.
62 	// Es ist kein Exponent dabei.
63 	var cl_idecoded_float decoded = integer_decode_float(arg);
64 	var const cl_I& significand = decoded.mantissa;
65 	var const cl_I& expon = decoded.exponent;
66 	var uintC mantprec = float_digits(arg)-1;
67 	var cl_I numerator = significand;
68 	var cl_I denominator = 1;
69 	var cl_I abrund_einh = 1; // Abrundungseinheit:
70 	       // Abrunden um 1 in der letzten abrundbaren Stelle entspricht
71 	       // einer Erniedrigung von numerator um abrund_einh.
72 	var cl_I aufrund_einh = 1; // Aufrundungseinheit:
73 	       // Aufrunden um 1 in der letzten aufrundbaren Stelle entspricht
74 	       // einer Erhöhung von numerator um aufrund_einh.
75 	digitstring.reset();
76 	if (expon > 0) {
77 		numerator = numerator << expon;
78 		aufrund_einh = abrund_einh = 1 << expon;
79 	}
80 	elif (expon < 0) {
81 		denominator = denominator << -expon;
82 		// aufrund_einh = abrund_einh = 1;
83 	}
84 	// Zahl = numerator/denominator
85 	if (significand == ash(1,mantprec)) {
86 		// Ist der Significand=2^mantprec, so ist abrund-einh zu halbieren.
87 		// Man kann stattdessen auch alle 3 anderen Grössen verdoppeln:
88 		aufrund_einh = aufrund_einh << 1;
89 		numerator = numerator << 1;
90 		denominator = denominator << 1;
91 	}
92 	// Defaultmäßig: Auf-/Abrunde-Einheit = eine Einheit in der letzten
93 	// BINÄRstelle.
94 	// Zahl = numerator/denominator
95 	// Skalierungsfaktor k in die Zahl mit einbeziehen (vgl. CLTL S.394)
96 	// k<0 -> Mantisse durch 10^|k| dividieren
97 	// k>0 -> Mantisse mit 10^k multiplizieren
98 	// Dabei aufrund-einh, abrund-einh im Verhältnis zu numerator beibehalten.
99 	if (k != 0) {
100 		if (k < 0) {
101 			var cl_I skal_faktor = expt_pos(10,-k);
102 			denominator = denominator * skal_faktor;
103 		}
104 		elif (k > 0) {
105 			var cl_I skal_faktor = expt_pos(10,k);
106 			numerator = numerator * skal_faktor;
107 			aufrund_einh = aufrund_einh * skal_faktor;
108 			abrund_einh = abrund_einh * skal_faktor;
109 		}
110 	}
111 	// Stellen: 0 = 1. Stelle vor dem Punkt, -1 = 1. Stelle nach dem Punkt.
112 	var sintL stelle = 0; // Stelle der als nächstes auszugebenden Ziffer
113 	// auf >= 1/10 adjustieren:
114 	// (jeweils numerator mit 10 multiplizieren, eine führende 0 mehr vorsehen)
115 	until (10*numerator >= denominator) {
116 		stelle = stelle-1;
117 		numerator = numerator * 10;
118 		aufrund_einh = aufrund_einh * 10;
119 		abrund_einh = abrund_einh * 10;
120 	}
121 	// stelle = Stelle der letzten führenden 0
122 	//        = 1 + Stelle der 1. signifikanten Ziffer
123 	//        oder =0, falls k>=0
124 	// Ausführung der Rundung:
125 	var bool letzte_stelle_p = false; // d oder width angegeben?
126 	var sintL letzte_stelle = 0; // falls d oder width angegeben waren:
127 				     // Stelle der letzten signifikanten Ziffer
128 	var bool halbzahlig = false; // zeigt an, ob hinten genau ein 0.500000 wegfällt
129 	do {
130 		// Solange das Ergebnis auch nach Aufrundung >= 1 bliebe,
131 		// eine Vorkommastelle mehr einplanen:
132 		until (((numerator << 1) + aufrund_einh) < (denominator << 1)) {
133 			denominator = denominator * 10;
134 			stelle = stelle+1;
135 		}
136 		// Falls d oder width angegeben:
137 		// letzte_stelle ausrechnen
138 		if (d != 0) {
139 			// Falls dmin angegeben: min(-d,-dmin) = -max(d,dmin).
140 			// Sonst -d.
141 			letzte_stelle = -d;
142 			if (dmin > 0)
143 				if (letzte_stelle > -dmin)
144 					letzte_stelle = -dmin;
145 			letzte_stelle_p = true;
146 		}
147 		elif (width > 0) {
148 			// Falls nicht d, nur width angegeben:
149 			if (stelle < 0)
150 				// Es kommen führende Nullen nach dem Punkt -> d:=width-1
151 				letzte_stelle = 1-width;
152 			else
153 				// Es kommen keine führenden Nullen nach dem Punkt ->
154 				// Es wird stelle Vorkommaziffern geben, d:=width-1-stelle
155 				letzte_stelle = 1+stelle-width;
156 			// also letzte_stelle = -(width-1 - max(stelle,0))
157 			// wieder dmin berücksichtigen:
158 			if (dmin > 0)
159 				if (letzte_stelle > -dmin)
160 					letzte_stelle = -dmin;
161 			letzte_stelle_p = true;
162 		}
163 		if (letzte_stelle_p) {
164 			var sintL ziffernzahl = letzte_stelle - stelle;
165 			// ziffernzahl = - Zahl signifikanter Stellen oder >=0.
166 			var cl_I dezimal_einh = denominator;
167 			// dezimal-einh := ceiling(dezimal_einh*expt(10,ziffernzahl))
168 			if (ziffernzahl > 0)
169 				dezimal_einh = dezimal_einh*expt_pos(10,ziffernzahl);
170 			elif (ziffernzahl < 0)
171 				dezimal_einh = ceiling1(dezimal_einh,expt_pos(10,-ziffernzahl));
172 			// dezimal-einh = Um wieviel numerator erhöht bzw. erniedigt werden
173 			// müßte, damit sich die Dezimaldarstellung um genau 1 an der
174 			// Position letzte_stelle verändert.
175 			if (abrund_einh < dezimal_einh)
176 				abrund_einh = dezimal_einh;
177 			if (aufrund_einh < dezimal_einh)
178 				aufrund_einh = dezimal_einh;
179 			// Jetzt darf auch um eine (halbe) DEZIMAL-Einheit gerundet werden.
180 			if (aufrund_einh == dezimal_einh)
181 				halbzahlig = true;
182 		}
183 	} until (((numerator << 1) + aufrund_einh) < (denominator << 1));
184 	// stelle = Position der ersten signifikanten Stelle + 1
185 	var uintL digit_count = 0; // Zahl der bisher in digit-string
186 	       // ausgegebenen Ziffern (exklusive den Punkt)
187 	var uintL point_pos = 0; // Punkt-Position = Zahl führender Stellen
188 			         // = Zahl der Ziffern vor dem Punkt
189 	// Führenden Punkt und nachfolgende Nullen ausgeben:
190 	if (stelle < 0) {
191 		digitstring.push('.');
192 		point_pos = digit_count;
193 		for (int i = -stelle; i >= 0; i--) {
194 			digitstring.push('0');
195 			digit_count++;
196 		}
197 	}
198 	// Ziffern der Mantisse ausgeben:
199 	var uintL digit; // die laufende Ziffer, >=0, <10
200 	var bool abrunden; // letzte Ziffer abzurunden?
201 	var bool aufrunden; // letzte Ziffer aufzurunden?
202 	for (;;) {
203 		if (stelle == 0) {
204 			digitstring.push('.');
205 			point_pos = digit_count;
206 		}
207 		stelle = stelle-1;
208 		var cl_I_div_t div = cl_divide(numerator*10,denominator);
209 		digit = cl_I_to_UL(div.quotient);
210 		numerator = div.remainder;
211 		abrund_einh = abrund_einh*10;
212 		aufrund_einh = aufrund_einh*10;
213 		abrunden = ((numerator<<1) < abrund_einh);
214 		aufrunden = (halbzahlig
215 			     ? (numerator<<1) >= (denominator<<1) - aufrund_einh
216 			     : (numerator<<1) > (denominator<<1) - aufrund_einh
217 			    );
218 		if (abrunden || aufrunden
219 		    || (letzte_stelle_p && (stelle <= letzte_stelle))
220 		   )
221 		   break;
222 		digitstring.push("0123456789"[digit]);
223 		digit_count++;
224 	}
225 	// letzte signifikante Ziffer ausgeben:
226 	if (letzte_stelle_p ? (stelle >= letzte_stelle) : true) {
227 		digit = (abrunden && !aufrunden ? digit :
228 			 aufrunden && !abrunden ? digit+1 :
229 			 (numerator<<1) <= denominator ? digit : digit+1);
230 		digitstring.push("0123456789"[digit]);
231 		digit_count++;
232 	}
233 	// Nachfolgende Nullen und Punkt ausgeben
234 	if (stelle >= 0) {
235 		for (int i = stelle; i >= 0; i--) {
236 			digitstring.push('0');
237 			digit_count++;
238 		}
239 		digitstring.push('.');
240 		point_pos = digit_count;
241 	}
242 	if (d != 0)
243 		for (int i = d - (digit_count - point_pos); i >= 0; i--) {
244 			digitstring.push('0');
245 			digit_count++;
246 		}
247 	return digits_with_dot(digitstring.contents(), digit_count+1,
248 			point_pos==0,
249 			point_pos==digit_count,
250 			point_pos
251 		);
252 }
253 
254 }  // namespace cln
255 
256