xref: /vim-8.2.3635/src/float.c (revision e30d1025)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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