fmt.c (8461B)
1 /* fmt.c - formato de resultados (ver fmt.h). */ 2 #include <string.h> 3 #include "fmt.h" 4 #include "str.h" 5 6 void fmt_default(Fmt *f) 7 { 8 f->mode = FMT_NORM1; 9 f->n = 0; 10 f->digits = 10; 11 f->mixed = false; 12 } 13 14 static int ndigits(int64_t v) 15 { 16 int n = 1; 17 if (v < 0) v = -v; 18 while (v >= 10) { v /= 10; n++; } 19 return n; 20 } 21 22 /* mantisa con 'nd' cifras significativas de un Dec ya redondeado */ 23 static void put_digits(Dec r, int intdig, int fracdig, char *out, int n) 24 { 25 char d[DEC_DIGITS + 1]; 26 dec_digits(r, d); 27 int p = 0; 28 if (r.neg) p = str_put(out, n, p, "-"); 29 /* parte entera: intdig digitos (el primero es d[0] si e >= 0) */ 30 int e = r.e; 31 char tmp[64]; 32 int k = 0; 33 if (e < 0) tmp[k++] = '0'; 34 else for (int i = 0; i <= e && k < 40; i++) tmp[k++] = i < DEC_DIGITS ? d[i] : '0'; 35 (void)intdig; 36 if (fracdig > 0) { 37 tmp[k++] = '.'; 38 for (int i = 1; i <= fracdig && k < 60; i++) { 39 int idx = e + i; /* posicion del digito en d */ 40 tmp[k++] = idx < 0 ? '0' : idx < DEC_DIGITS ? d[idx] : '0'; 41 } 42 } 43 tmp[k] = 0; 44 str_put(out, n, p, tmp); 45 } 46 47 static void strip_zeros(char *s) 48 { 49 char *dot = strchr(s, '.'); 50 if (!dot) return; 51 int l = (int)strlen(s); 52 while (l > 0 && s[l - 1] == '0') s[--l] = 0; 53 if (l > 0 && s[l - 1] == '.') s[--l] = 0; 54 } 55 56 void fmt_dec(Dec v, const Fmt *f, char *mant, int n, int *exp, bool *sci) 57 { 58 *sci = false; 59 *exp = 0; 60 if (v.err) { str_put(mant, n, 0, "Error"); return; } 61 int mode = f->mode; 62 if (!v.m) { 63 if (mode == FMT_FIX) { put_digits(v, 1, f->n, mant, n); return; } 64 str_put(mant, n, 0, "0"); 65 return; 66 } 67 if (mode == FMT_FIX) { 68 Dec r = dec_round_dec(v, f->n); 69 if (r.m && r.e < 10) { 70 if (!r.m) r = DEC_ZERO; 71 put_digits(r, 0, f->n, mant, n); 72 return; 73 } 74 mode = FMT_NORM1; /* demasiado grande: Norm */ 75 } 76 if (mode == FMT_SCI || mode == FMT_ENG) { 77 int sig = mode == FMT_SCI ? (f->n ? f->n : 10) : f->digits; 78 Dec r = dec_round_sig(v, sig); 79 int e = r.e, shift = 0; 80 if (mode == FMT_ENG) { shift = ((e % 3) + 3) % 3; e -= shift; } 81 Dec m = r; 82 m.e = (int16_t)shift; 83 put_digits(m, 0, sig - 1 - shift > 0 ? sig - 1 - shift : 0, mant, n); 84 if (mode == FMT_ENG) strip_zeros(mant); 85 *exp = e; 86 *sci = true; 87 return; 88 } 89 int sig = f->digits; 90 Dec r = dec_round_sig(v, sig); 91 int lo = mode == FMT_NORM1 ? -2 : -9; 92 if (r.e >= 10 || r.e < lo) { 93 Dec m = r; 94 m.e = 0; 95 put_digits(m, 0, sig - 1, mant, n); 96 strip_zeros(mant); 97 *exp = r.e; 98 *sci = true; 99 return; 100 } 101 int frac = sig - 1 - r.e; 102 put_digits(r, 0, frac > 0 ? frac : 0, mant, n); 103 strip_zeros(mant); 104 } 105 106 /* ------------------------------------------------------------ formas exactas */ 107 108 static bool show_frac(const Ex *x) { return x->d > 1 && ndigits(x->a) + ndigits(x->d) <= 10; } 109 110 static bool exact_ok(const Num *v) 111 { 112 if (!v->exact) return false; 113 const Ex *x = &v->x; 114 if (x->p) return x->b == 0 && (x->a < 0 ? -x->a : x->a) < 1000 && x->d < 1000; 115 if (x->b) { 116 int64_t a = x->a < 0 ? -x->a : x->a, b = x->b < 0 ? -x->b : x->b; 117 return a < 1000 && b < 1000 && x->r < 10000 && x->d < 1000; 118 } 119 return x->d == 1 || show_frac(x); 120 } 121 122 bool fmt_has_exact(const Num *v, const Fmt *f) 123 { 124 (void)f; 125 if (!exact_ok(v)) return false; 126 /* un entero se ve igual en exacto y en decimal */ 127 return !(ex_is_int(&v->x)); 128 } 129 130 static void put_int(Expr *e, int64_t v) 131 { 132 char b[24]; 133 i64_text(b, sizeof b, 0, v); 134 for (char *p = b; *p; p++) ex_insert_raw(e, (unsigned char)*p); 135 } 136 137 /* b√r (b = ±1 se muestra como √r) */ 138 static void put_surd(Expr *e, int64_t b, int64_t r) 139 { 140 if (b == -1) ex_insert_raw(e, '-'); 141 else if (b != 1) put_int(e, b); 142 ex_insert_raw(e, T_SQRT); 143 put_int(e, r); 144 ex_insert_raw(e, T_END); 145 } 146 147 /* numerador de la forma exacta (sin el denominador) */ 148 static void put_numer(Expr *e, const Ex *x, bool abs) 149 { 150 int64_t a = x->a, b = x->b; 151 if (abs && (x->b ? (a < 0 || (a == 0 && b < 0)) : a < 0)) { a = -a; b = -b; } 152 if (x->p) { 153 if (a == -1) ex_insert_raw(e, '-'); 154 else if (a != 1) put_int(e, a); 155 ex_insert_raw(e, T_PI); 156 return; 157 } 158 if (!b) { put_int(e, a); return; } 159 if (a) { 160 put_int(e, a); 161 if (b > 0) ex_insert_raw(e, '+'); 162 } 163 put_surd(e, b, x->r); 164 } 165 166 static void put_dec(Expr *e, Dec v, const Fmt *f) 167 { 168 char m[64]; 169 int exp; 170 bool sci; 171 fmt_dec(v, f, m, sizeof m, &exp, &sci); 172 for (char *p = m; *p; p++) ex_insert_raw(e, (unsigned char)*p); 173 if (sci) { 174 ex_insert_raw(e, T_MUL); 175 ex_insert_raw(e, '1'); 176 ex_insert_raw(e, '0'); 177 ex_insert_raw(e, T_POW); 178 put_int(e, exp); 179 ex_insert_raw(e, T_END); 180 } 181 } 182 183 bool fmt_result(const Num *v, const Fmt *f, bool want_dec, Expr *out) 184 { 185 ex_clear(out); 186 if (want_dec || !exact_ok(v) || ex_is_int(&v->x)) { 187 put_dec(out, v->v, f); 188 return false; 189 } 190 const Ex *x = &v->x; 191 if (x->d == 1) { put_numer(out, x, false); return true; } 192 /* negativo: el signo va afuera de la fraccion (salvo con a y b de distinto signo) */ 193 bool neg = x->b ? (x->a < 0 || (x->a == 0 && x->b < 0)) : x->a < 0; 194 if (x->b && x->a < 0 && x->b > 0) neg = false; 195 if (neg) ex_insert_raw(out, '-'); 196 if (f->mixed && ex_is_rat(x)) { 197 int64_t a = x->a < 0 ? -x->a : x->a; 198 if (a > x->d) { 199 ex_insert_raw(out, T_MIXED); 200 put_int(out, a / x->d); 201 ex_insert_raw(out, T_SEP); 202 put_int(out, a % x->d); 203 ex_insert_raw(out, T_SEP); 204 put_int(out, x->d); 205 ex_insert_raw(out, T_END); 206 return true; 207 } 208 } 209 ex_insert_raw(out, T_FRAC); 210 put_numer(out, x, neg); 211 ex_insert_raw(out, T_SEP); 212 put_int(out, x->d); 213 ex_insert_raw(out, T_END); 214 return true; 215 } 216 217 void fmt_dms(Dec v, Expr *out) 218 { 219 ex_clear(out); 220 if (v.err) return; 221 bool neg = v.neg; 222 v = dec_abs(v); 223 /* todo en centesimas de segundo para redondear una sola vez */ 224 Dec cs = dec_round_int(dec_mul_int(v, 360000)); 225 int64_t t; 226 if (!dec_to_int(cs, &t)) { put_dec(out, v, &(Fmt){ FMT_NORM1, 0, 10, false }); return; } 227 int64_t d = t / 360000, m = t / 6000 % 60, s = t % 6000; 228 if (neg) ex_insert_raw(out, '-'); 229 put_int(out, d); 230 ex_insert_raw(out, T_DEGS); 231 put_int(out, m); 232 ex_insert_raw(out, T_MIN); 233 put_int(out, s / 100); 234 if (s % 100) { 235 ex_insert_raw(out, '.'); 236 ex_insert_raw(out, '0' + (int)(s % 100 / 10)); 237 if (s % 10) ex_insert_raw(out, '0' + (int)(s % 10)); 238 } 239 ex_insert_raw(out, T_SEC); 240 } 241 242 int fmt_plain(const Num *v, const Fmt *f, bool want_dec, char *buf, int n) 243 { 244 static Expr e; 245 fmt_result(v, f, want_dec, &e); 246 /* texto lineal legible: fracciones como a/b, raices como √r, potencias de 10 con E */ 247 int p = 0; 248 bool paren = false; 249 buf[0] = 0; 250 for (int i = 0; i < e.n; i++) { 251 int t = e.t[i]; 252 const char *s = 0; 253 char c[2] = { (char)t, 0 }; 254 switch (t) { 255 case T_FRAC: { 256 /* numerador con + o - adentro: entre parentesis */ 257 bool op = false; 258 for (int k = i + 2; k < e.n && e.t[k] != T_SEP; k++) if (e.t[k] == '+' || e.t[k] == '-') op = true; 259 s = op ? "(" : ""; 260 if (op) paren = true; 261 break; 262 } 263 case T_MIXED: s = ""; break; 264 case T_SEP: { 265 int fld, ct = ex_container(&e, i, &fld); 266 s = ct >= 0 && e.t[ct] == T_MIXED && fld == 0 ? " " : paren ? ")/" : "/"; 267 paren = false; 268 break; 269 } 270 case T_END: s = ""; break; 271 case T_SQRT: s = "\xe2\x88\x9a"; break; 272 case T_PI: s = "\xcf\x80"; break; 273 case T_DEGS: s = "\xc2\xb0"; break; 274 case T_MIN: s = "\xe2\x80\xb2"; break; 275 case T_SEC: s = "\xe2\x80\xb3"; break; 276 case T_MUL: 277 if (i + 3 < e.n && e.t[i + 3] == T_POW) { s = "E"; i += 3; } 278 else s = "\xc3\x97"; 279 break; 280 default: s = c; 281 } 282 p = str_put(buf, n, p, s); 283 if (p < 0) break; 284 } 285 return p; 286 }