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