1 /* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10 /*
11 * float.c: Floating point functions
12 */
13 #define USING_FLOAT_STUFF
14
15 #include "vim.h"
16
17 #if (defined(FEAT_EVAL) && defined(FEAT_FLOAT)) || defined(PROTO)
18
19 #ifdef VMS
20 # include <float.h>
21 #endif
22
23 /*
24 * Convert the string "text" to a floating point number.
25 * This uses strtod(). setlocale(LC_NUMERIC, "C") has been used to make sure
26 * this always uses a decimal point.
27 * Returns the length of the text that was consumed.
28 */
29 int
string2float(char_u * text,float_T * value,int skip_quotes)30 string2float(
31 char_u *text,
32 float_T *value, // result stored here
33 int skip_quotes)
34 {
35 char *s = (char *)text;
36 float_T f;
37
38 // MS-Windows does not deal with "inf" and "nan" properly.
39 if (STRNICMP(text, "inf", 3) == 0)
40 {
41 *value = INFINITY;
42 return 3;
43 }
44 if (STRNICMP(text, "-inf", 3) == 0)
45 {
46 *value = -INFINITY;
47 return 4;
48 }
49 if (STRNICMP(text, "nan", 3) == 0)
50 {
51 *value = NAN;
52 return 3;
53 }
54 if (skip_quotes && vim_strchr((char_u *)s, '\'') != NULL)
55 {
56 char_u buf[100];
57 char_u *p = buf;
58 int quotes = 0;
59
60 vim_strncpy(buf, (char_u *)s, 99);
61 p = buf;
62 for (;;)
63 {
64 // remove single quotes between digits, not in the exponent
65 if (*p == '\'')
66 {
67 ++quotes;
68 mch_memmove(p, p + 1, STRLEN(p));
69 }
70 if (!vim_isdigit(*p))
71 break;
72 p = skipdigits(p);
73 }
74 s = (char *)buf;
75 f = strtod(s, &s);
76 *value = f;
77 return (int)((char_u *)s - buf) + quotes;
78 }
79
80 f = strtod(s, &s);
81 *value = f;
82 return (int)((char_u *)s - text);
83 }
84
85 /*
86 * Get the float value of "argvars[0]" into "f".
87 * Returns FAIL when the argument is not a Number or Float.
88 */
89 static int
get_float_arg(typval_T * argvars,float_T * f)90 get_float_arg(typval_T *argvars, float_T *f)
91 {
92 if (argvars[0].v_type == VAR_FLOAT)
93 {
94 *f = argvars[0].vval.v_float;
95 return OK;
96 }
97 if (argvars[0].v_type == VAR_NUMBER)
98 {
99 *f = (float_T)argvars[0].vval.v_number;
100 return OK;
101 }
102 emsg(_("E808: Number or Float required"));
103 return FAIL;
104 }
105
106 /*
107 * "abs(expr)" function
108 */
109 void
f_abs(typval_T * argvars,typval_T * rettv)110 f_abs(typval_T *argvars, typval_T *rettv)
111 {
112 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
113 return;
114
115 if (argvars[0].v_type == VAR_FLOAT)
116 {
117 rettv->v_type = VAR_FLOAT;
118 rettv->vval.v_float = fabs(argvars[0].vval.v_float);
119 }
120 else
121 {
122 varnumber_T n;
123 int error = FALSE;
124
125 n = tv_get_number_chk(&argvars[0], &error);
126 if (error)
127 rettv->vval.v_number = -1;
128 else if (n > 0)
129 rettv->vval.v_number = n;
130 else
131 rettv->vval.v_number = -n;
132 }
133 }
134
135 /*
136 * "acos()" function
137 */
138 void
f_acos(typval_T * argvars,typval_T * rettv)139 f_acos(typval_T *argvars, typval_T *rettv)
140 {
141 float_T f = 0.0;
142
143 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
144 return;
145
146 rettv->v_type = VAR_FLOAT;
147 if (get_float_arg(argvars, &f) == OK)
148 rettv->vval.v_float = acos(f);
149 else
150 rettv->vval.v_float = 0.0;
151 }
152
153 /*
154 * "asin()" function
155 */
156 void
f_asin(typval_T * argvars,typval_T * rettv)157 f_asin(typval_T *argvars, typval_T *rettv)
158 {
159 float_T f = 0.0;
160
161 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
162 return;
163
164 rettv->v_type = VAR_FLOAT;
165 if (get_float_arg(argvars, &f) == OK)
166 rettv->vval.v_float = asin(f);
167 else
168 rettv->vval.v_float = 0.0;
169 }
170
171 /*
172 * "atan()" function
173 */
174 void
f_atan(typval_T * argvars,typval_T * rettv)175 f_atan(typval_T *argvars, typval_T *rettv)
176 {
177 float_T f = 0.0;
178
179 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
180 return;
181
182 rettv->v_type = VAR_FLOAT;
183 if (get_float_arg(argvars, &f) == OK)
184 rettv->vval.v_float = atan(f);
185 else
186 rettv->vval.v_float = 0.0;
187 }
188
189 /*
190 * "atan2()" function
191 */
192 void
f_atan2(typval_T * argvars,typval_T * rettv)193 f_atan2(typval_T *argvars, typval_T *rettv)
194 {
195 float_T fx = 0.0, fy = 0.0;
196
197 if (in_vim9script()
198 && (check_for_float_or_nr_arg(argvars, 0) == FAIL
199 || check_for_float_or_nr_arg(argvars, 1) == FAIL))
200 return;
201
202 rettv->v_type = VAR_FLOAT;
203 if (get_float_arg(argvars, &fx) == OK
204 && get_float_arg(&argvars[1], &fy) == OK)
205 rettv->vval.v_float = atan2(fx, fy);
206 else
207 rettv->vval.v_float = 0.0;
208 }
209
210 /*
211 * "ceil({float})" function
212 */
213 void
f_ceil(typval_T * argvars,typval_T * rettv)214 f_ceil(typval_T *argvars, typval_T *rettv)
215 {
216 float_T f = 0.0;
217
218 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
219 return;
220
221 rettv->v_type = VAR_FLOAT;
222 if (get_float_arg(argvars, &f) == OK)
223 rettv->vval.v_float = ceil(f);
224 else
225 rettv->vval.v_float = 0.0;
226 }
227
228 /*
229 * "cos()" function
230 */
231 void
f_cos(typval_T * argvars,typval_T * rettv)232 f_cos(typval_T *argvars, typval_T *rettv)
233 {
234 float_T f = 0.0;
235
236 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
237 return;
238
239 rettv->v_type = VAR_FLOAT;
240 if (get_float_arg(argvars, &f) == OK)
241 rettv->vval.v_float = cos(f);
242 else
243 rettv->vval.v_float = 0.0;
244 }
245
246 /*
247 * "cosh()" function
248 */
249 void
f_cosh(typval_T * argvars,typval_T * rettv)250 f_cosh(typval_T *argvars, typval_T *rettv)
251 {
252 float_T f = 0.0;
253
254 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
255 return;
256
257 rettv->v_type = VAR_FLOAT;
258 if (get_float_arg(argvars, &f) == OK)
259 rettv->vval.v_float = cosh(f);
260 else
261 rettv->vval.v_float = 0.0;
262 }
263
264 /*
265 * "exp()" function
266 */
267 void
f_exp(typval_T * argvars,typval_T * rettv)268 f_exp(typval_T *argvars, typval_T *rettv)
269 {
270 float_T f = 0.0;
271
272 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
273 return;
274
275 rettv->v_type = VAR_FLOAT;
276 if (get_float_arg(argvars, &f) == OK)
277 rettv->vval.v_float = exp(f);
278 else
279 rettv->vval.v_float = 0.0;
280 }
281
282 /*
283 * "float2nr({float})" function
284 */
285 void
f_float2nr(typval_T * argvars,typval_T * rettv)286 f_float2nr(typval_T *argvars, typval_T *rettv)
287 {
288 float_T f = 0.0;
289
290 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
291 return;
292
293 if (get_float_arg(argvars, &f) == OK)
294 {
295 if (f <= (float_T)-VARNUM_MAX + DBL_EPSILON)
296 rettv->vval.v_number = -VARNUM_MAX;
297 else if (f >= (float_T)VARNUM_MAX - DBL_EPSILON)
298 rettv->vval.v_number = VARNUM_MAX;
299 else
300 rettv->vval.v_number = (varnumber_T)f;
301 }
302 }
303
304 /*
305 * "floor({float})" function
306 */
307 void
f_floor(typval_T * argvars,typval_T * rettv)308 f_floor(typval_T *argvars, typval_T *rettv)
309 {
310 float_T f = 0.0;
311
312 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
313 return;
314
315 rettv->v_type = VAR_FLOAT;
316 if (get_float_arg(argvars, &f) == OK)
317 rettv->vval.v_float = floor(f);
318 else
319 rettv->vval.v_float = 0.0;
320 }
321
322 /*
323 * "fmod()" function
324 */
325 void
f_fmod(typval_T * argvars,typval_T * rettv)326 f_fmod(typval_T *argvars, typval_T *rettv)
327 {
328 float_T fx = 0.0, fy = 0.0;
329
330 if (in_vim9script()
331 && (check_for_float_or_nr_arg(argvars, 0) == FAIL
332 || check_for_float_or_nr_arg(argvars, 1) == FAIL))
333 return;
334
335 rettv->v_type = VAR_FLOAT;
336 if (get_float_arg(argvars, &fx) == OK
337 && get_float_arg(&argvars[1], &fy) == OK)
338 rettv->vval.v_float = fmod(fx, fy);
339 else
340 rettv->vval.v_float = 0.0;
341 }
342
343 # if defined(HAVE_MATH_H) || defined(PROTO)
344 /*
345 * "isinf()" function
346 */
347 void
f_isinf(typval_T * argvars,typval_T * rettv)348 f_isinf(typval_T *argvars, typval_T *rettv)
349 {
350 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
351 return;
352
353 if (argvars[0].v_type == VAR_FLOAT && isinf(argvars[0].vval.v_float))
354 rettv->vval.v_number = argvars[0].vval.v_float > 0.0 ? 1 : -1;
355 }
356
357 /*
358 * "isnan()" function
359 */
360 void
f_isnan(typval_T * argvars,typval_T * rettv)361 f_isnan(typval_T *argvars, typval_T *rettv)
362 {
363 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
364 return;
365
366 rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT
367 && isnan(argvars[0].vval.v_float);
368 }
369 # endif
370
371 /*
372 * "log()" function
373 */
374 void
f_log(typval_T * argvars,typval_T * rettv)375 f_log(typval_T *argvars, typval_T *rettv)
376 {
377 float_T f = 0.0;
378
379 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
380 return;
381
382 rettv->v_type = VAR_FLOAT;
383 if (get_float_arg(argvars, &f) == OK)
384 rettv->vval.v_float = log(f);
385 else
386 rettv->vval.v_float = 0.0;
387 }
388
389 /*
390 * "log10()" function
391 */
392 void
f_log10(typval_T * argvars,typval_T * rettv)393 f_log10(typval_T *argvars, typval_T *rettv)
394 {
395 float_T f = 0.0;
396
397 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
398 return;
399
400 rettv->v_type = VAR_FLOAT;
401 if (get_float_arg(argvars, &f) == OK)
402 rettv->vval.v_float = log10(f);
403 else
404 rettv->vval.v_float = 0.0;
405 }
406
407 /*
408 * "pow()" function
409 */
410 void
f_pow(typval_T * argvars,typval_T * rettv)411 f_pow(typval_T *argvars, typval_T *rettv)
412 {
413 float_T fx = 0.0, fy = 0.0;
414
415 if (in_vim9script()
416 && (check_for_float_or_nr_arg(argvars, 0) == FAIL
417 || check_for_float_or_nr_arg(argvars, 1) == FAIL))
418 return;
419
420 rettv->v_type = VAR_FLOAT;
421 if (get_float_arg(argvars, &fx) == OK
422 && get_float_arg(&argvars[1], &fy) == OK)
423 rettv->vval.v_float = pow(fx, fy);
424 else
425 rettv->vval.v_float = 0.0;
426 }
427
428
429 /*
430 * round() is not in C90, use ceil() or floor() instead.
431 */
432 float_T
vim_round(float_T f)433 vim_round(float_T f)
434 {
435 return f > 0 ? floor(f + 0.5) : ceil(f - 0.5);
436 }
437
438 /*
439 * "round({float})" function
440 */
441 void
f_round(typval_T * argvars,typval_T * rettv)442 f_round(typval_T *argvars, typval_T *rettv)
443 {
444 float_T f = 0.0;
445
446 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
447 return;
448
449 rettv->v_type = VAR_FLOAT;
450 if (get_float_arg(argvars, &f) == OK)
451 rettv->vval.v_float = vim_round(f);
452 else
453 rettv->vval.v_float = 0.0;
454 }
455
456 /*
457 * "sin()" function
458 */
459 void
f_sin(typval_T * argvars,typval_T * rettv)460 f_sin(typval_T *argvars, typval_T *rettv)
461 {
462 float_T f = 0.0;
463
464 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
465 return;
466
467 rettv->v_type = VAR_FLOAT;
468 if (get_float_arg(argvars, &f) == OK)
469 rettv->vval.v_float = sin(f);
470 else
471 rettv->vval.v_float = 0.0;
472 }
473
474 /*
475 * "sinh()" function
476 */
477 void
f_sinh(typval_T * argvars,typval_T * rettv)478 f_sinh(typval_T *argvars, typval_T *rettv)
479 {
480 float_T f = 0.0;
481
482 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
483 return;
484
485 rettv->v_type = VAR_FLOAT;
486 if (get_float_arg(argvars, &f) == OK)
487 rettv->vval.v_float = sinh(f);
488 else
489 rettv->vval.v_float = 0.0;
490 }
491
492 /*
493 * "sqrt()" function
494 */
495 void
f_sqrt(typval_T * argvars,typval_T * rettv)496 f_sqrt(typval_T *argvars, typval_T *rettv)
497 {
498 float_T f = 0.0;
499
500 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
501 return;
502
503 rettv->v_type = VAR_FLOAT;
504 if (get_float_arg(argvars, &f) == OK)
505 rettv->vval.v_float = sqrt(f);
506 else
507 rettv->vval.v_float = 0.0;
508 }
509
510 /*
511 * "str2float()" function
512 */
513 void
f_str2float(typval_T * argvars,typval_T * rettv)514 f_str2float(typval_T *argvars, typval_T *rettv)
515 {
516 char_u *p;
517 int isneg;
518 int skip_quotes;
519
520 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
521 return;
522
523 skip_quotes = argvars[1].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[1]);
524
525 p = skipwhite(tv_get_string_strict(&argvars[0]));
526 isneg = (*p == '-');
527
528 if (*p == '+' || *p == '-')
529 p = skipwhite(p + 1);
530 (void)string2float(p, &rettv->vval.v_float, skip_quotes);
531 if (isneg)
532 rettv->vval.v_float *= -1;
533 rettv->v_type = VAR_FLOAT;
534 }
535
536 /*
537 * "tan()" function
538 */
539 void
f_tan(typval_T * argvars,typval_T * rettv)540 f_tan(typval_T *argvars, typval_T *rettv)
541 {
542 float_T f = 0.0;
543
544 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
545 return;
546
547 rettv->v_type = VAR_FLOAT;
548 if (get_float_arg(argvars, &f) == OK)
549 rettv->vval.v_float = tan(f);
550 else
551 rettv->vval.v_float = 0.0;
552 }
553
554 /*
555 * "tanh()" function
556 */
557 void
f_tanh(typval_T * argvars,typval_T * rettv)558 f_tanh(typval_T *argvars, typval_T *rettv)
559 {
560 float_T f = 0.0;
561
562 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
563 return;
564
565 rettv->v_type = VAR_FLOAT;
566 if (get_float_arg(argvars, &f) == OK)
567 rettv->vval.v_float = tanh(f);
568 else
569 rettv->vval.v_float = 0.0;
570 }
571
572 /*
573 * "trunc({float})" function
574 */
575 void
f_trunc(typval_T * argvars,typval_T * rettv)576 f_trunc(typval_T *argvars, typval_T *rettv)
577 {
578 float_T f = 0.0;
579
580 if (in_vim9script() && check_for_float_or_nr_arg(argvars, 0) == FAIL)
581 return;
582
583 rettv->v_type = VAR_FLOAT;
584 if (get_float_arg(argvars, &f) == OK)
585 // trunc() is not in C90, use floor() or ceil() instead.
586 rettv->vval.v_float = f > 0 ? floor(f) : ceil(f);
587 else
588 rettv->vval.v_float = 0.0;
589 }
590
591 #endif
592