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