Coverage Report

Created: 2022-10-06 21:30

/src/php-src/ext/standard/math.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright (c) The PHP Group                                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to version 3.01 of the PHP license,      |
6
   | that is bundled with this package in the file LICENSE, and is        |
7
   | available through the world-wide-web at the following url:           |
8
   | http://www.php.net/license/3_01.txt                                  |
9
   | If you did not receive a copy of the PHP license and are unable to   |
10
   | obtain it through the world-wide-web, please send a note to          |
11
   | license@php.net so we can mail you a copy immediately.               |
12
   +----------------------------------------------------------------------+
13
   | Authors: Jim Winstead <jimw@php.net>                                 |
14
   |          Stig Sæther Bakken <ssb@php.net>                            |
15
   |          Zeev Suraski <zeev@php.net>                                 |
16
   | PHP 4.0 patches by Thies C. Arntzen <thies@thieso.net>               |
17
   +----------------------------------------------------------------------+
18
*/
19
20
#include "php.h"
21
#include "php_math.h"
22
#include "zend_multiply.h"
23
#include "zend_exceptions.h"
24
#include "zend_portability.h"
25
26
#include <math.h>
27
#include <float.h>
28
#include <stdlib.h>
29
30
#include "basic_functions.h"
31
32
/* {{{ php_intlog10abs
33
   Returns floor(log10(fabs(val))), uses fast binary search */
34
0
static inline int php_intlog10abs(double value) {
35
0
  int result;
36
0
  value = fabs(value);
37
38
0
  if (value < 1e-8 || value > 1e22) {
39
0
    result = (int)floor(log10(value));
40
0
  } else {
41
0
    static const double values[] = {
42
0
      1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1,
43
0
      1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
44
0
      1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
45
0
      1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
46
    /* Do a binary search with 5 steps */
47
0
    result = 15;
48
0
    if (value < values[result]) {
49
0
      result -= 8;
50
0
    } else {
51
0
      result += 8;
52
0
    }
53
0
    if (value < values[result]) {
54
0
      result -= 4;
55
0
    } else {
56
0
      result += 4;
57
0
    }
58
0
    if (value < values[result]) {
59
0
      result -= 2;
60
0
    } else {
61
0
      result += 2;
62
0
    }
63
0
    if (value < values[result]) {
64
0
      result -= 1;
65
0
    } else {
66
0
      result += 1;
67
0
    }
68
0
    if (value < values[result]) {
69
0
      result -= 1;
70
0
    }
71
0
    result -= 8;
72
0
  }
73
0
  return result;
74
0
}
75
/* }}} */
76
77
/* {{{ php_intpow10
78
       Returns pow(10.0, (double)power), uses fast lookup table for exact powers */
79
0
static inline double php_intpow10(int power) {
80
0
  static const double powers[] = {
81
0
    1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
82
0
    1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
83
0
    1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
84
85
  /* Not in lookup table */
86
0
  if (power < 0 || power > 22) {
87
0
    return pow(10.0, (double)power);
88
0
  }
89
0
  return powers[power];
90
0
}
91
/* }}} */
92
93
/* {{{ php_round_helper
94
       Actually performs the rounding of a value to integer in a certain mode */
95
0
static inline double php_round_helper(double value, int mode) {
96
0
  double tmp_value;
97
98
0
  if (value >= 0.0) {
99
0
    tmp_value = floor(value + 0.5);
100
0
    if ((mode == PHP_ROUND_HALF_DOWN && value == (-0.5 + tmp_value)) ||
101
0
      (mode == PHP_ROUND_HALF_EVEN && value == (0.5 + 2 * floor(tmp_value/2.0))) ||
102
0
      (mode == PHP_ROUND_HALF_ODD  && value == (0.5 + 2 * floor(tmp_value/2.0) - 1.0)))
103
0
    {
104
0
      tmp_value = tmp_value - 1.0;
105
0
    }
106
0
  } else {
107
0
    tmp_value = ceil(value - 0.5);
108
0
    if ((mode == PHP_ROUND_HALF_DOWN && value == (0.5 + tmp_value)) ||
109
0
      (mode == PHP_ROUND_HALF_EVEN && value == (-0.5 + 2 * ceil(tmp_value/2.0))) ||
110
0
      (mode == PHP_ROUND_HALF_ODD  && value == (-0.5 + 2 * ceil(tmp_value/2.0) + 1.0)))
111
0
    {
112
0
      tmp_value = tmp_value + 1.0;
113
0
    }
114
0
  }
115
116
0
  return tmp_value;
117
0
}
118
/* }}} */
119
120
/* {{{ _php_math_round */
121
/*
122
 * Rounds a number to a certain number of decimal places in a certain rounding
123
 * mode. For the specifics of the algorithm, see http://wiki.php.net/rfc/rounding
124
 */
125
0
PHPAPI double _php_math_round(double value, int places, int mode) {
126
0
  double f1, f2;
127
0
  double tmp_value;
128
0
  int precision_places;
129
130
0
  if (!zend_finite(value) || value == 0.0) {
131
0
    return value;
132
0
  }
133
134
0
  places = places < INT_MIN+1 ? INT_MIN+1 : places;
135
0
  precision_places = 14 - php_intlog10abs(value);
136
137
0
  f1 = php_intpow10(abs(places));
138
139
  /* If the decimal precision guaranteed by FP arithmetic is higher than
140
     the requested places BUT is small enough to make sure a non-zero value
141
     is returned, pre-round the result to the precision */
142
0
  if (precision_places > places && precision_places - 15 < places) {
143
0
    int64_t use_precision = precision_places < INT_MIN+1 ? INT_MIN+1 : precision_places;
144
145
0
    f2 = php_intpow10(abs((int)use_precision));
146
0
    if (use_precision >= 0) {
147
0
      tmp_value = value * f2;
148
0
    } else {
149
0
      tmp_value = value / f2;
150
0
    }
151
    /* preround the result (tmp_value will always be something * 1e14,
152
       thus never larger than 1e15 here) */
153
0
    tmp_value = php_round_helper(tmp_value, mode);
154
155
0
    use_precision = places - precision_places;
156
0
    use_precision = use_precision < INT_MIN+1 ? INT_MIN+1 : use_precision;
157
    /* now correctly move the decimal point */
158
0
    f2 = php_intpow10(abs((int)use_precision));
159
    /* because places < precision_places */
160
0
    tmp_value = tmp_value / f2;
161
0
  } else {
162
    /* adjust the value */
163
0
    if (places >= 0) {
164
0
      tmp_value = value * f1;
165
0
    } else {
166
0
      tmp_value = value / f1;
167
0
    }
168
    /* This value is beyond our precision, so rounding it is pointless */
169
0
    if (fabs(tmp_value) >= 1e15) {
170
0
      return value;
171
0
    }
172
0
  }
173
174
  /* round the temp value */
175
0
  tmp_value = php_round_helper(tmp_value, mode);
176
177
  /* see if it makes sense to use simple division to round the value */
178
0
  if (abs(places) < 23) {
179
0
    if (places > 0) {
180
0
      tmp_value = tmp_value / f1;
181
0
    } else {
182
0
      tmp_value = tmp_value * f1;
183
0
    }
184
0
  } else {
185
    /* Simple division can't be used since that will cause wrong results.
186
       Instead, the number is converted to a string and back again using
187
       strtod(). strtod() will return the nearest possible FP value for
188
       that string. */
189
190
    /* 40 Bytes should be more than enough for this format string. The
191
       float won't be larger than 1e15 anyway. But just in case, use
192
       snprintf() and make sure the buffer is zero-terminated */
193
0
    char buf[40];
194
0
    snprintf(buf, 39, "%15fe%d", tmp_value, -places);
195
0
    buf[39] = '\0';
196
0
    tmp_value = zend_strtod(buf, NULL);
197
    /* couldn't convert to string and back */
198
0
    if (!zend_finite(tmp_value) || zend_isnan(tmp_value)) {
199
0
      tmp_value = value;
200
0
    }
201
0
  }
202
203
0
  return tmp_value;
204
0
}
205
/* }}} */
206
207
/* {{{ Return the absolute value of the number */
208
PHP_FUNCTION(abs)
209
0
{
210
0
  zval *value;
211
212
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
213
0
    Z_PARAM_NUMBER(value)
214
0
  ZEND_PARSE_PARAMETERS_END();
215
216
0
  if (Z_TYPE_P(value) == IS_DOUBLE) {
217
0
    RETURN_DOUBLE(fabs(Z_DVAL_P(value)));
218
0
  } else if (Z_TYPE_P(value) == IS_LONG) {
219
0
    if (Z_LVAL_P(value) == ZEND_LONG_MIN) {
220
0
      RETURN_DOUBLE(-(double)ZEND_LONG_MIN);
221
0
    } else {
222
0
      RETURN_LONG(Z_LVAL_P(value) < 0 ? -Z_LVAL_P(value) : Z_LVAL_P(value));
223
0
    }
224
0
  } else {
225
0
    ZEND_ASSERT(0 && "Unexpected type");
226
0
  }
227
0
}
228
/* }}} */
229
230
/* {{{ Returns the next highest integer value of the number */
231
PHP_FUNCTION(ceil)
232
0
{
233
0
  zval *value;
234
235
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
236
0
    Z_PARAM_NUMBER(value)
237
0
  ZEND_PARSE_PARAMETERS_END();
238
239
0
  if (Z_TYPE_P(value) == IS_DOUBLE) {
240
0
    RETURN_DOUBLE(ceil(Z_DVAL_P(value)));
241
0
  } else if (Z_TYPE_P(value) == IS_LONG) {
242
0
    RETURN_DOUBLE(zval_get_double(value));
243
0
  } else {
244
0
    ZEND_ASSERT(0 && "Unexpected type");
245
0
  }
246
0
}
247
/* }}} */
248
249
/* {{{ Returns the next lowest integer value from the number */
250
PHP_FUNCTION(floor)
251
137
{
252
137
  zval *value;
253
254
411
  ZEND_PARSE_PARAMETERS_START(1, 1)
255
137
    Z_PARAM_NUMBER(value)
256
137
  ZEND_PARSE_PARAMETERS_END();
257
258
137
  if (Z_TYPE_P(value) == IS_DOUBLE) {
259
0
    RETURN_DOUBLE(floor(Z_DVAL_P(value)));
260
137
  } else if (Z_TYPE_P(value) == IS_LONG) {
261
137
    RETURN_DOUBLE(zval_get_double(value));
262
0
  } else {
263
0
    ZEND_ASSERT(0 && "Unexpected type");
264
0
  }
265
137
}
266
/* }}} */
267
268
/* {{{ Returns the number rounded to specified precision */
269
PHP_FUNCTION(round)
270
0
{
271
0
  zval *value;
272
0
  int places = 0;
273
0
  zend_long precision = 0;
274
0
  zend_long mode = PHP_ROUND_HALF_UP;
275
0
  double return_val;
276
277
0
  ZEND_PARSE_PARAMETERS_START(1, 3)
278
0
    Z_PARAM_NUMBER(value)
279
0
    Z_PARAM_OPTIONAL
280
0
    Z_PARAM_LONG(precision)
281
0
    Z_PARAM_LONG(mode)
282
0
  ZEND_PARSE_PARAMETERS_END();
283
284
0
  if (ZEND_NUM_ARGS() >= 2) {
285
0
#if SIZEOF_ZEND_LONG > SIZEOF_INT
286
0
    if (precision >= 0) {
287
0
      places = precision > INT_MAX ? INT_MAX : (int)precision;
288
0
    } else {
289
0
      places = precision <= INT_MIN ? INT_MIN+1 : (int)precision;
290
0
    }
291
#else
292
    places = precision;
293
#endif
294
0
  }
295
296
0
  switch (Z_TYPE_P(value)) {
297
0
    case IS_LONG:
298
      /* Simple case - long that doesn't need to be rounded. */
299
0
      if (places >= 0) {
300
0
        RETURN_DOUBLE((double) Z_LVAL_P(value));
301
0
      }
302
      /* break omitted intentionally */
303
304
0
    case IS_DOUBLE:
305
0
      return_val = (Z_TYPE_P(value) == IS_LONG) ? (double)Z_LVAL_P(value) : Z_DVAL_P(value);
306
0
      return_val = _php_math_round(return_val, (int)places, (int)mode);
307
0
      RETURN_DOUBLE(return_val);
308
0
      break;
309
310
0
    EMPTY_SWITCH_DEFAULT_CASE()
311
0
  }
312
0
}
313
/* }}} */
314
315
/* {{{ Returns the sine of the number in radians */
316
PHP_FUNCTION(sin)
317
94
{
318
94
  double num;
319
320
280
  ZEND_PARSE_PARAMETERS_START(1, 1)
321
92
    Z_PARAM_DOUBLE(num)
322
94
  ZEND_PARSE_PARAMETERS_END();
323
92
  RETURN_DOUBLE(sin(num));
324
92
}
325
/* }}} */
326
327
/* {{{ Returns the cosine of the number in radians */
328
PHP_FUNCTION(cos)
329
485
{
330
485
  double num;
331
332
1.45k
  ZEND_PARSE_PARAMETERS_START(1, 1)
333
485
    Z_PARAM_DOUBLE(num)
334
485
  ZEND_PARSE_PARAMETERS_END();
335
485
  RETURN_DOUBLE(cos(num));
336
485
}
337
/* }}} */
338
339
/* {{{ Returns the tangent of the number in radians */
340
PHP_FUNCTION(tan)
341
256
{
342
256
  double num;
343
344
768
  ZEND_PARSE_PARAMETERS_START(1, 1)
345
256
    Z_PARAM_DOUBLE(num)
346
256
  ZEND_PARSE_PARAMETERS_END();
347
256
  RETURN_DOUBLE(tan(num));
348
256
}
349
/* }}} */
350
351
/* {{{ Returns the arc sine of the number in radians */
352
PHP_FUNCTION(asin)
353
0
{
354
0
  double num;
355
356
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
357
0
    Z_PARAM_DOUBLE(num)
358
0
  ZEND_PARSE_PARAMETERS_END();
359
0
  RETURN_DOUBLE(asin(num));
360
0
}
361
/* }}} */
362
363
/* {{{ Return the arc cosine of the number in radians */
364
PHP_FUNCTION(acos)
365
0
{
366
0
  double num;
367
368
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
369
0
    Z_PARAM_DOUBLE(num)
370
0
  ZEND_PARSE_PARAMETERS_END();
371
0
  RETURN_DOUBLE(acos(num));
372
0
}
373
/* }}} */
374
375
/* {{{ Returns the arc tangent of the number in radians */
376
PHP_FUNCTION(atan)
377
0
{
378
0
  double num;
379
380
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
381
0
    Z_PARAM_DOUBLE(num)
382
0
  ZEND_PARSE_PARAMETERS_END();
383
0
  RETURN_DOUBLE(atan(num));
384
0
}
385
/* }}} */
386
387
/* {{{ Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x */
388
PHP_FUNCTION(atan2)
389
0
{
390
0
  double num1, num2;
391
392
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
393
0
    Z_PARAM_DOUBLE(num1)
394
0
    Z_PARAM_DOUBLE(num2)
395
0
  ZEND_PARSE_PARAMETERS_END();
396
0
  RETURN_DOUBLE(atan2(num1, num2));
397
0
}
398
/* }}} */
399
400
/* {{{ Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2 */
401
PHP_FUNCTION(sinh)
402
0
{
403
0
  double num;
404
405
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
406
0
    Z_PARAM_DOUBLE(num)
407
0
  ZEND_PARSE_PARAMETERS_END();
408
0
  RETURN_DOUBLE(sinh(num));
409
0
}
410
/* }}} */
411
412
/* {{{ Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2 */
413
PHP_FUNCTION(cosh)
414
0
{
415
0
  double num;
416
417
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
418
0
    Z_PARAM_DOUBLE(num)
419
0
  ZEND_PARSE_PARAMETERS_END();
420
0
  RETURN_DOUBLE(cosh(num));
421
0
}
422
/* }}} */
423
424
/* {{{ Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number) */
425
PHP_FUNCTION(tanh)
426
0
{
427
0
  double num;
428
429
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
430
0
    Z_PARAM_DOUBLE(num)
431
0
  ZEND_PARSE_PARAMETERS_END();
432
0
  RETURN_DOUBLE(tanh(num));
433
0
}
434
/* }}} */
435
436
/* {{{ Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number */
437
PHP_FUNCTION(asinh)
438
0
{
439
0
  double num;
440
441
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
442
0
    Z_PARAM_DOUBLE(num)
443
0
  ZEND_PARSE_PARAMETERS_END();
444
0
  RETURN_DOUBLE(asinh(num));
445
0
}
446
/* }}} */
447
448
/* {{{ Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number */
449
PHP_FUNCTION(acosh)
450
0
{
451
0
  double num;
452
453
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
454
0
    Z_PARAM_DOUBLE(num)
455
0
  ZEND_PARSE_PARAMETERS_END();
456
0
  RETURN_DOUBLE(acosh(num));
457
0
}
458
/* }}} */
459
460
/* {{{ Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number */
461
PHP_FUNCTION(atanh)
462
0
{
463
0
  double num;
464
465
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
466
0
    Z_PARAM_DOUBLE(num)
467
0
  ZEND_PARSE_PARAMETERS_END();
468
0
  RETURN_DOUBLE(atanh(num));
469
0
}
470
/* }}} */
471
472
/* {{{ Returns an approximation of pi */
473
PHP_FUNCTION(pi)
474
13
{
475
13
  ZEND_PARSE_PARAMETERS_NONE();
476
477
9
  RETURN_DOUBLE(M_PI);
478
9
}
479
/* }}} */
480
481
/* {{{ Returns whether argument is finite */
482
PHP_FUNCTION(is_finite)
483
51
{
484
51
  double dval;
485
486
153
  ZEND_PARSE_PARAMETERS_START(1, 1)
487
51
    Z_PARAM_DOUBLE(dval)
488
51
  ZEND_PARSE_PARAMETERS_END();
489
51
  RETURN_BOOL(zend_finite(dval));
490
51
}
491
/* }}} */
492
493
/* {{{ Returns whether argument is infinite */
494
PHP_FUNCTION(is_infinite)
495
0
{
496
0
  double dval;
497
498
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
499
0
    Z_PARAM_DOUBLE(dval)
500
0
  ZEND_PARSE_PARAMETERS_END();
501
0
  RETURN_BOOL(zend_isinf(dval));
502
0
}
503
/* }}} */
504
505
/* {{{ Returns whether argument is not a number */
506
PHP_FUNCTION(is_nan)
507
14.1k
{
508
14.1k
  double dval;
509
510
42.4k
  ZEND_PARSE_PARAMETERS_START(1, 1)
511
14.1k
    Z_PARAM_DOUBLE(dval)
512
14.1k
  ZEND_PARSE_PARAMETERS_END();
513
14.0k
  RETURN_BOOL(zend_isnan(dval));
514
14.0k
}
515
/* }}} */
516
517
/* {{{ Returns base raised to the power of exponent. Returns integer result when possible */
518
PHP_FUNCTION(pow)
519
1
{
520
1
  zval *zbase, *zexp;
521
522
2
  ZEND_PARSE_PARAMETERS_START(2, 2)
523
0
    Z_PARAM_ZVAL(zbase)
524
0
    Z_PARAM_ZVAL(zexp)
525
1
  ZEND_PARSE_PARAMETERS_END();
526
527
0
  pow_function(return_value, zbase, zexp);
528
0
}
529
/* }}} */
530
531
/* {{{ Returns e raised to the power of the number */
532
PHP_FUNCTION(exp)
533
1
{
534
1
  double num;
535
536
3
  ZEND_PARSE_PARAMETERS_START(1, 1)
537
1
    Z_PARAM_DOUBLE(num)
538
1
  ZEND_PARSE_PARAMETERS_END();
539
540
1
  RETURN_DOUBLE(exp(num));
541
1
}
542
/* }}} */
543
544
/* {{{ Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
545
PHP_FUNCTION(expm1)
546
0
{
547
0
  double num;
548
549
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
550
0
    Z_PARAM_DOUBLE(num)
551
0
  ZEND_PARSE_PARAMETERS_END();
552
553
0
  RETURN_DOUBLE(expm1(num));
554
0
}
555
/* }}} */
556
557
/* {{{ Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */
558
PHP_FUNCTION(log1p)
559
3
{
560
3
  double num;
561
562
9
  ZEND_PARSE_PARAMETERS_START(1, 1)
563
3
    Z_PARAM_DOUBLE(num)
564
3
  ZEND_PARSE_PARAMETERS_END();
565
566
3
  RETURN_DOUBLE(log1p(num));
567
3
}
568
/* }}} */
569
570
/* {{{ Returns the natural logarithm of the number, or the base log if base is specified */
571
PHP_FUNCTION(log)
572
140
{
573
140
  double num, base = 0;
574
575
420
  ZEND_PARSE_PARAMETERS_START(1, 2)
576
140
    Z_PARAM_DOUBLE(num)
577
140
    Z_PARAM_OPTIONAL
578
0
    Z_PARAM_DOUBLE(base)
579
140
  ZEND_PARSE_PARAMETERS_END();
580
581
140
  if (ZEND_NUM_ARGS() == 1) {
582
140
    RETURN_DOUBLE(log(num));
583
140
  }
584
585
0
  if (base == 2.0) {
586
0
    RETURN_DOUBLE(log2(num));
587
0
  }
588
589
0
  if (base == 10.0) {
590
0
    RETURN_DOUBLE(log10(num));
591
0
  }
592
593
0
  if (base == 1.0) {
594
0
    RETURN_DOUBLE(ZEND_NAN);
595
0
  }
596
597
0
  if (base <= 0.0) {
598
0
    zend_argument_value_error(2, "must be greater than 0");
599
0
    RETURN_THROWS();
600
0
  }
601
602
0
  RETURN_DOUBLE(log(num) / log(base));
603
0
}
604
/* }}} */
605
606
/* {{{ Returns the base-10 logarithm of the number */
607
PHP_FUNCTION(log10)
608
352
{
609
352
  double num;
610
611
1.05k
  ZEND_PARSE_PARAMETERS_START(1, 1)
612
351
    Z_PARAM_DOUBLE(num)
613
352
  ZEND_PARSE_PARAMETERS_END();
614
615
351
  RETURN_DOUBLE(log10(num));
616
351
}
617
/* }}} */
618
619
/* {{{ Returns the square root of the number */
620
PHP_FUNCTION(sqrt)
621
528
{
622
528
  double num;
623
624
1.58k
  ZEND_PARSE_PARAMETERS_START(1, 1)
625
524
    Z_PARAM_DOUBLE(num)
626
528
  ZEND_PARSE_PARAMETERS_END();
627
628
524
  RETURN_DOUBLE(sqrt(num));
629
524
}
630
/* }}} */
631
632
/* {{{ Returns sqrt(num1*num1 + num2*num2) */
633
PHP_FUNCTION(hypot)
634
0
{
635
0
  double num1, num2;
636
637
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
638
0
    Z_PARAM_DOUBLE(num1)
639
0
    Z_PARAM_DOUBLE(num2)
640
0
  ZEND_PARSE_PARAMETERS_END();
641
642
0
  RETURN_DOUBLE(hypot(num1, num2));
643
0
}
644
/* }}} */
645
646
/* {{{ Converts the number in degrees to the radian equivalent */
647
PHP_FUNCTION(deg2rad)
648
207
{
649
207
  double deg;
650
651
621
  ZEND_PARSE_PARAMETERS_START(1, 1)
652
207
    Z_PARAM_DOUBLE(deg)
653
207
  ZEND_PARSE_PARAMETERS_END();
654
207
  RETURN_DOUBLE((deg / 180.0) * M_PI);
655
207
}
656
/* }}} */
657
658
/* {{{ Converts the radian number to the equivalent number in degrees */
659
PHP_FUNCTION(rad2deg)
660
0
{
661
0
  double rad;
662
663
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
664
0
    Z_PARAM_DOUBLE(rad)
665
0
  ZEND_PARSE_PARAMETERS_END();
666
667
0
  RETURN_DOUBLE((rad / M_PI) * 180);
668
0
}
669
/* }}} */
670
671
/* {{{ _php_math_basetolong */
672
/*
673
 * Convert a string representation of a base(2-36) number to a long.
674
 */
675
PHPAPI zend_long _php_math_basetolong(zval *arg, int base)
676
0
{
677
0
  zend_long num = 0, digit, onum;
678
0
  zend_long i;
679
0
  char c, *s;
680
681
0
  if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
682
0
    return 0;
683
0
  }
684
685
0
  s = Z_STRVAL_P(arg);
686
687
0
  for (i = Z_STRLEN_P(arg); i > 0; i--) {
688
0
    c = *s++;
689
690
0
    digit = (c >= '0' && c <= '9') ? c - '0'
691
0
      : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
692
0
      : (c >= 'a' && c <= 'z') ? c - 'a' + 10
693
0
      : base;
694
695
0
    if (digit >= base) {
696
0
      continue;
697
0
    }
698
699
0
    onum = num;
700
0
    num = num * base + digit;
701
0
    if (num > onum)
702
0
      continue;
703
704
0
    {
705
706
0
      php_error_docref(NULL, E_WARNING, "Number '%s' is too big to fit in long", s);
707
0
      return ZEND_LONG_MAX;
708
0
    }
709
0
  }
710
711
0
  return num;
712
0
}
713
/* }}} */
714
715
/* {{{ _php_math_basetozval */
716
/*
717
 * Convert a string representation of a base(2-36) number to a zval.
718
 */
719
PHPAPI void _php_math_basetozval(zend_string *str, int base, zval *ret)
720
0
{
721
0
  zend_long num = 0;
722
0
  double fnum = 0;
723
0
  int mode = 0;
724
0
  char c, *s, *e;
725
0
  zend_long cutoff;
726
0
  int cutlim;
727
0
  int invalidchars = 0;
728
729
0
  s = ZSTR_VAL(str);
730
0
  e = s + ZSTR_LEN(str);
731
732
  /* Skip leading whitespace */
733
0
  while (s < e && isspace(*s)) s++;
734
  /* Skip trailing whitespace */
735
0
  while (s < e && isspace(*(e-1))) e--;
736
737
0
  if (e - s >= 2) {
738
0
    if (base == 16 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) s += 2;
739
0
    if (base == 8 && s[0] == '0' && (s[1] == 'o' || s[1] == 'O')) s += 2;
740
0
    if (base == 2 && s[0] == '0' && (s[1] == 'b' || s[1] == 'B')) s += 2;
741
0
  }
742
743
0
  cutoff = ZEND_LONG_MAX / base;
744
0
  cutlim = ZEND_LONG_MAX % base;
745
746
0
  while (s < e) {
747
0
    c = *s++;
748
749
    /* might not work for EBCDIC */
750
0
    if (c >= '0' && c <= '9')
751
0
      c -= '0';
752
0
    else if (c >= 'A' && c <= 'Z')
753
0
      c -= 'A' - 10;
754
0
    else if (c >= 'a' && c <= 'z')
755
0
      c -= 'a' - 10;
756
0
    else {
757
0
      invalidchars++;
758
0
      continue;
759
0
    }
760
761
0
    if (c >= base) {
762
0
      invalidchars++;
763
0
      continue;
764
0
    }
765
766
0
    switch (mode) {
767
0
    case 0: /* Integer */
768
0
      if (num < cutoff || (num == cutoff && c <= cutlim)) {
769
0
        num = num * base + c;
770
0
        break;
771
0
      } else {
772
0
        fnum = (double)num;
773
0
        mode = 1;
774
0
      }
775
      /* fall-through */
776
0
    case 1: /* Float */
777
0
      fnum = fnum * base + c;
778
0
    }
779
0
  }
780
781
0
  if (invalidchars > 0) {
782
0
    zend_error(E_DEPRECATED, "Invalid characters passed for attempted conversion, these have been ignored");
783
0
  }
784
785
0
  if (mode == 1) {
786
0
    ZVAL_DOUBLE(ret, fnum);
787
0
  } else {
788
0
    ZVAL_LONG(ret, num);
789
0
  }
790
0
}
791
/* }}} */
792
793
/* {{{ _php_math_longtobase */
794
/*
795
 * Convert a long to a string containing a base(2-36) representation of
796
 * the number.
797
 */
798
PHPAPI zend_string * _php_math_longtobase(zend_long arg, int base)
799
0
{
800
0
  static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
801
0
  char buf[(sizeof(zend_ulong) << 3) + 1];
802
0
  char *ptr, *end;
803
0
  zend_ulong value;
804
805
0
  if (base < 2 || base > 36) {
806
0
    return ZSTR_EMPTY_ALLOC();
807
0
  }
808
809
0
  value = arg;
810
811
0
  end = ptr = buf + sizeof(buf) - 1;
812
0
  *ptr = '\0';
813
814
0
  do {
815
0
    ZEND_ASSERT(ptr > buf);
816
0
    *--ptr = digits[value % base];
817
0
    value /= base;
818
0
  } while (value);
819
820
0
  return zend_string_init(ptr, end - ptr, 0);
821
0
}
822
/* }}} */
823
824
/* {{{ _php_math_zvaltobase */
825
/*
826
 * Convert a zval to a string containing a base(2-36) representation of
827
 * the number.
828
 */
829
PHPAPI zend_string * _php_math_zvaltobase(zval *arg, int base)
830
0
{
831
0
  static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
832
833
0
  if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
834
0
    return ZSTR_EMPTY_ALLOC();
835
0
  }
836
837
0
  if (Z_TYPE_P(arg) == IS_DOUBLE) {
838
0
    double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
839
0
    char *ptr, *end;
840
0
    char buf[(sizeof(double) << 3) + 1];
841
842
    /* Don't try to convert +/- infinity */
843
0
    if (fvalue == ZEND_INFINITY || fvalue == -ZEND_INFINITY) {
844
0
      php_error_docref(NULL, E_WARNING, "Number too large");
845
0
      return ZSTR_EMPTY_ALLOC();
846
0
    }
847
848
0
    end = ptr = buf + sizeof(buf) - 1;
849
0
    *ptr = '\0';
850
851
0
    do {
852
0
      *--ptr = digits[(int) fmod(fvalue, base)];
853
0
      fvalue /= base;
854
0
    } while (ptr > buf && fabs(fvalue) >= 1);
855
856
0
    return zend_string_init(ptr, end - ptr, 0);
857
0
  }
858
859
0
  return _php_math_longtobase(Z_LVAL_P(arg), base);
860
0
}
861
/* }}} */
862
863
/* {{{ Returns the decimal equivalent of the binary number */
864
PHP_FUNCTION(bindec)
865
0
{
866
0
  zend_string *arg;
867
868
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
869
0
    Z_PARAM_STR(arg)
870
0
  ZEND_PARSE_PARAMETERS_END();
871
872
0
  _php_math_basetozval(arg, 2, return_value);
873
0
}
874
/* }}} */
875
876
/* {{{ Returns the decimal equivalent of the hexadecimal number */
877
PHP_FUNCTION(hexdec)
878
0
{
879
0
  zend_string *arg;
880
881
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
882
0
    Z_PARAM_STR(arg)
883
0
  ZEND_PARSE_PARAMETERS_END();
884
885
0
  _php_math_basetozval(arg, 16, return_value);
886
0
}
887
/* }}} */
888
889
/* {{{ Returns the decimal equivalent of an octal string */
890
PHP_FUNCTION(octdec)
891
0
{
892
0
  zend_string *arg;
893
894
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
895
0
    Z_PARAM_STR(arg)
896
0
  ZEND_PARSE_PARAMETERS_END();
897
898
0
  _php_math_basetozval(arg, 8, return_value);
899
0
}
900
/* }}} */
901
902
/* {{{ Returns a string containing a binary representation of the number */
903
PHP_FUNCTION(decbin)
904
0
{
905
0
  zend_long arg;
906
907
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
908
0
    Z_PARAM_LONG(arg)
909
0
  ZEND_PARSE_PARAMETERS_END();
910
911
0
  RETURN_STR(_php_math_longtobase(arg, 2));
912
0
}
913
/* }}} */
914
915
/* {{{ Returns a string containing an octal representation of the given number */
916
PHP_FUNCTION(decoct)
917
0
{
918
0
  zend_long arg;
919
920
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
921
0
    Z_PARAM_LONG(arg)
922
0
  ZEND_PARSE_PARAMETERS_END();
923
924
0
  RETURN_STR(_php_math_longtobase(arg, 8));
925
0
}
926
/* }}} */
927
928
/* {{{ Returns a string containing a hexadecimal representation of the given number */
929
PHP_FUNCTION(dechex)
930
0
{
931
0
  zend_long arg;
932
933
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
934
0
    Z_PARAM_LONG(arg)
935
0
  ZEND_PARSE_PARAMETERS_END();
936
937
0
  RETURN_STR(_php_math_longtobase(arg, 16));
938
0
}
939
/* }}} */
940
941
/* {{{ Converts a number in a string from any base <= 36 to any base <= 36 */
942
PHP_FUNCTION(base_convert)
943
0
{
944
0
  zval temp;
945
0
  zend_string *number;
946
0
  zend_long frombase, tobase;
947
0
  zend_string *result;
948
949
0
  ZEND_PARSE_PARAMETERS_START(3, 3)
950
0
    Z_PARAM_STR(number)
951
0
    Z_PARAM_LONG(frombase)
952
0
    Z_PARAM_LONG(tobase)
953
0
  ZEND_PARSE_PARAMETERS_END();
954
955
0
  if (frombase < 2 || frombase > 36) {
956
0
    zend_argument_value_error(2, "must be between 2 and 36 (inclusive)");
957
0
    RETURN_THROWS();
958
0
  }
959
0
  if (tobase < 2 || tobase > 36) {
960
0
    zend_argument_value_error(3, "must be between 2 and 36 (inclusive)");
961
0
    RETURN_THROWS();
962
0
  }
963
964
0
  _php_math_basetozval(number, (int)frombase, &temp);
965
0
  result = _php_math_zvaltobase(&temp, (int)tobase);
966
0
  RETVAL_STR(result);
967
0
}
968
/* }}} */
969
970
/* {{{ _php_math_number_format */
971
PHPAPI zend_string *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
972
0
{
973
0
  return _php_math_number_format_ex(d, dec, &dec_point, 1, &thousand_sep, 1);
974
0
}
975
976
PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, const char *dec_point,
977
    size_t dec_point_len, const char *thousand_sep, size_t thousand_sep_len)
978
0
{
979
0
  zend_string *res;
980
0
  zend_string *tmpbuf;
981
0
  char *s, *t;  /* source, target */
982
0
  char *dp;
983
0
  size_t integral;
984
0
  size_t reslen = 0;
985
0
  int count = 0;
986
0
  int is_negative=0;
987
988
0
  if (d < 0) {
989
0
    is_negative = 1;
990
0
    d = -d;
991
0
  }
992
993
0
  dec = MAX(0, dec);
994
0
  d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
995
0
  tmpbuf = strpprintf(0, "%.*F", dec, d);
996
0
  if (tmpbuf == NULL) {
997
0
    return NULL;
998
0
  } else if (!isdigit((int)ZSTR_VAL(tmpbuf)[0])) {
999
0
    return tmpbuf;
1000
0
  }
1001
1002
  /* Check if the number is no longer negative after rounding */
1003
0
  if (is_negative && d == 0) {
1004
0
    is_negative = 0;
1005
0
  }
1006
1007
  /* find decimal point, if expected */
1008
0
  if (dec) {
1009
0
    dp = strpbrk(ZSTR_VAL(tmpbuf), ".,");
1010
0
  } else {
1011
0
    dp = NULL;
1012
0
  }
1013
1014
  /* calculate the length of the return buffer */
1015
0
  if (dp) {
1016
0
    integral = (dp - ZSTR_VAL(tmpbuf));
1017
0
  } else {
1018
    /* no decimal point was found */
1019
0
    integral = ZSTR_LEN(tmpbuf);
1020
0
  }
1021
1022
  /* allow for thousand separators */
1023
0
  if (thousand_sep) {
1024
0
    integral = zend_safe_addmult((integral-1)/3, thousand_sep_len, integral, "number formatting");
1025
0
  }
1026
1027
0
  reslen = integral;
1028
1029
0
  if (dec) {
1030
0
    reslen += dec;
1031
1032
0
    if (dec_point) {
1033
0
      reslen = zend_safe_addmult(reslen, 1, dec_point_len, "number formatting");
1034
0
    }
1035
0
  }
1036
1037
  /* add a byte for minus sign */
1038
0
  if (is_negative) {
1039
0
    reslen++;
1040
0
  }
1041
0
  res = zend_string_alloc(reslen, 0);
1042
1043
0
  s = ZSTR_VAL(tmpbuf) + ZSTR_LEN(tmpbuf) - 1;
1044
0
  t = ZSTR_VAL(res) + reslen;
1045
0
  *t-- = '\0';
1046
1047
  /* copy the decimal places.
1048
   * Take care, as the sprintf implementation may return less places than
1049
   * we requested due to internal buffer limitations */
1050
0
  if (dec) {
1051
0
    size_t declen = (dp ? s - dp : 0);
1052
0
    size_t topad = (size_t)dec > declen ? dec - declen : 0;
1053
1054
    /* pad with '0's */
1055
0
    while (topad--) {
1056
0
      *t-- = '0';
1057
0
    }
1058
1059
0
    if (dp) {
1060
0
      s -= declen + 1; /* +1 to skip the point */
1061
0
      t -= declen;
1062
1063
      /* now copy the chars after the point */
1064
0
      memcpy(t + 1, dp + 1, declen);
1065
0
    }
1066
1067
    /* add decimal point */
1068
0
    if (dec_point) {
1069
0
      t -= dec_point_len;
1070
0
      memcpy(t + 1, dec_point, dec_point_len);
1071
0
    }
1072
0
  }
1073
1074
  /* copy the numbers before the decimal point, adding thousand
1075
   * separator every three digits */
1076
0
  while (s >= ZSTR_VAL(tmpbuf)) {
1077
0
    *t-- = *s--;
1078
0
    if (thousand_sep && (++count%3)==0 && s >= ZSTR_VAL(tmpbuf)) {
1079
0
      t -= thousand_sep_len;
1080
0
      memcpy(t + 1, thousand_sep, thousand_sep_len);
1081
0
    }
1082
0
  }
1083
1084
  /* and a minus sign, if needed */
1085
0
  if (is_negative) {
1086
0
    *t-- = '-';
1087
0
  }
1088
1089
0
  ZSTR_LEN(res) = reslen;
1090
0
  zend_string_release_ex(tmpbuf, 0);
1091
0
  return res;
1092
0
}
1093
1094
/* {{{ Formats a number with grouped thousands */
1095
PHP_FUNCTION(number_format)
1096
0
{
1097
0
  double num;
1098
0
  zend_long dec = 0;
1099
0
  char *thousand_sep = NULL, *dec_point = NULL;
1100
0
  size_t thousand_sep_len = 0, dec_point_len = 0;
1101
1102
0
  ZEND_PARSE_PARAMETERS_START(1, 4)
1103
0
    Z_PARAM_DOUBLE(num)
1104
0
    Z_PARAM_OPTIONAL
1105
0
    Z_PARAM_LONG(dec)
1106
0
    Z_PARAM_STRING_OR_NULL(dec_point, dec_point_len)
1107
0
    Z_PARAM_STRING_OR_NULL(thousand_sep, thousand_sep_len)
1108
0
  ZEND_PARSE_PARAMETERS_END();
1109
1110
0
  if (dec_point == NULL) {
1111
0
    dec_point = ".";
1112
0
    dec_point_len = 1;
1113
0
  }
1114
0
  if (thousand_sep == NULL) {
1115
0
    thousand_sep = ",";
1116
0
    thousand_sep_len = 1;
1117
0
  }
1118
1119
0
  RETURN_STR(_php_math_number_format_ex(num, (int)dec, dec_point, dec_point_len, thousand_sep, thousand_sep_len));
1120
0
}
1121
/* }}} */
1122
1123
/* {{{ Returns the remainder of dividing x by y as a float */
1124
PHP_FUNCTION(fmod)
1125
0
{
1126
0
  double num1, num2;
1127
1128
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
1129
0
    Z_PARAM_DOUBLE(num1)
1130
0
    Z_PARAM_DOUBLE(num2)
1131
0
  ZEND_PARSE_PARAMETERS_END();
1132
1133
0
  RETURN_DOUBLE(fmod(num1, num2));
1134
0
}
1135
/* }}} */
1136
1137
/* {{{ Perform floating-point division of dividend / divisor
1138
   with IEEE-754 semantics for division by zero. */
1139
#ifdef __clang__
1140
__attribute__((no_sanitize("float-divide-by-zero")))
1141
#endif
1142
PHP_FUNCTION(fdiv)
1143
0
{
1144
0
  double dividend, divisor;
1145
1146
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
1147
0
    Z_PARAM_DOUBLE(dividend)
1148
0
    Z_PARAM_DOUBLE(divisor)
1149
0
  ZEND_PARSE_PARAMETERS_END();
1150
1151
0
  RETURN_DOUBLE(dividend / divisor);
1152
0
}
1153
/* }}} */
1154
1155
/* {{{ Returns the integer quotient of the division of dividend by divisor */
1156
PHP_FUNCTION(intdiv)
1157
13.1k
{
1158
13.1k
  zend_long dividend, divisor;
1159
1160
39.4k
  ZEND_PARSE_PARAMETERS_START(2, 2)
1161
13.1k
    Z_PARAM_LONG(dividend)
1162
26.2k
    Z_PARAM_LONG(divisor)
1163
13.1k
  ZEND_PARSE_PARAMETERS_END();
1164
1165
13.1k
  if (divisor == 0) {
1166
10
    zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
1167
10
    RETURN_THROWS();
1168
13.1k
  } else if (divisor == -1 && dividend == ZEND_LONG_MIN) {
1169
    /* Prevent overflow error/crash ... really should not happen:
1170
       We don't return a float here as that violates function contract */
1171
0
    zend_throw_exception_ex(zend_ce_arithmetic_error, 0, "Division of PHP_INT_MIN by -1 is not an integer");
1172
0
    RETURN_THROWS();
1173
0
  }
1174
1175
13.1k
  RETURN_LONG(dividend / divisor);
1176
13.1k
}
1177
/* }}} */