Coverage Report

Created: 2026-07-25 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/standard/math.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Authors: Jim Winstead <jimw@php.net>                                 |
12
   |          Stig Sæther Bakken <ssb@php.net>                            |
13
   |          Zeev Suraski <zeev@php.net>                                 |
14
   | PHP 4.0 patches by Thies C. Arntzen <thies@thieso.net>               |
15
   +----------------------------------------------------------------------+
16
*/
17
18
#include "php.h"
19
#include "php_math.h"
20
#include "zend_bitset.h"
21
#include "zend_enum.h"
22
#include "zend_exceptions.h"
23
#include "zend_multiply.h"
24
#include "zend_portability.h"
25
#include "zend_strtod.h"
26
27
#include <float.h>
28
#include <math.h>
29
#include <stdlib.h>
30
31
#include "basic_functions.h"
32
33
PHPAPI zend_class_entry *rounding_mode_ce;
34
35
/* {{{ php_intpow10
36
       Returns pow(10.0, (double)power), uses fast lookup table for exact powers */
37
7
static inline double php_intpow10(int power) {
38
  /* Not in lookup table */
39
7
  if (power < 0 || power > 22) {
40
0
    return pow(10.0, (double)power);
41
0
  }
42
43
7
  static const double powers[] = {
44
7
      1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
45
7
      1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
46
47
7
  return powers[power];
48
7
}
49
/* }}} */
50
51
static zend_always_inline double php_round_get_basic_edge_case(double integral, double exponent, int places)
52
7
{
53
7
  return (places > 0)
54
7
    ? fabs((integral + copysign(0.5, integral)) / exponent)
55
7
    : fabs((integral + copysign(0.5, integral)) * exponent);
56
7
}
57
58
static zend_always_inline double php_round_get_zero_edge_case(double integral, double exponent, int places)
59
0
{
60
0
  return (places > 0)
61
0
    ? fabs((integral) / exponent)
62
0
    : fabs((integral) * exponent);
63
0
}
64
65
/* {{{ php_round_helper
66
     Actually performs the rounding of a value to integer in a certain mode */
67
7
static inline double php_round_helper(double integral, double value, double exponent, int places, int mode) {
68
7
  double value_abs = fabs(value);
69
7
  double edge_case;
70
71
7
  switch (mode) {
72
7
    case PHP_ROUND_HALF_UP:
73
7
      edge_case = php_round_get_basic_edge_case(integral, exponent, places);
74
7
      if (value_abs >= edge_case) {
75
        /* We must increase the magnitude of the integral part
76
         * (rounding up / towards infinity). copysign(1.0, integral)
77
         * will either result in 1.0 or -1.0 depending on the sign
78
         * of the input, thus increasing the magnitude, but without
79
         * generating branches in the assembly.
80
         *
81
         * This pattern is equally used for all the other modes.
82
         */
83
5
        return integral + copysign(1.0, integral);
84
5
      }
85
86
2
      return integral;
87
88
0
    case PHP_ROUND_HALF_DOWN:
89
0
      edge_case = php_round_get_basic_edge_case(integral, exponent, places);
90
0
      if (value_abs > edge_case) {
91
0
        return integral + copysign(1.0, integral);
92
0
      }
93
94
0
      return integral;
95
96
0
    case PHP_ROUND_CEILING:
97
0
      edge_case = php_round_get_zero_edge_case(integral, exponent, places);
98
0
      if (value > 0.0 && value_abs > edge_case) {
99
0
        return integral + 1.0;
100
0
      }
101
102
0
      return integral;
103
104
0
    case PHP_ROUND_FLOOR:
105
0
      edge_case = php_round_get_zero_edge_case(integral, exponent, places);
106
0
      if (value < 0.0 && value_abs > edge_case) {
107
0
        return integral - 1.0;
108
0
      }
109
110
0
      return integral;
111
112
0
    case PHP_ROUND_TOWARD_ZERO:
113
0
      return integral;
114
115
0
    case PHP_ROUND_AWAY_FROM_ZERO:
116
0
      edge_case = php_round_get_zero_edge_case(integral, exponent, places);
117
0
      if (value_abs > edge_case) {
118
0
        return integral + copysign(1.0, integral);
119
0
      }
120
121
0
      return integral;
122
123
0
    case PHP_ROUND_HALF_EVEN:
124
0
      edge_case = php_round_get_basic_edge_case(integral, exponent, places);
125
0
      if (value_abs > edge_case) {
126
0
        return integral + copysign(1.0, integral);
127
0
      } else if (UNEXPECTED(value_abs == edge_case)) {
128
0
        bool even = !fmod(integral, 2.0);
129
130
        /* If the integral part is not even we can make it even
131
         * by adding one in the direction of the existing sign.
132
         */
133
0
        if (!even) {
134
0
          return integral + copysign(1.0, integral);
135
0
        }
136
0
      }
137
138
0
      return integral;
139
140
0
    case PHP_ROUND_HALF_ODD:
141
0
      edge_case = php_round_get_basic_edge_case(integral, exponent, places);
142
0
      if (value_abs > edge_case) {
143
0
        return integral + copysign(1.0, integral);
144
0
      } else if (UNEXPECTED(value_abs == edge_case)) {
145
0
        bool even = !fmod(integral, 2.0);
146
147
0
        if (even) {
148
0
          return integral + copysign(1.0, integral);
149
0
        }
150
0
      }
151
152
0
      return integral;
153
154
0
    default: ZEND_UNREACHABLE();
155
7
  }
156
  // FIXME: GCC bug, branch is considered reachable.
157
0
  ZEND_UNREACHABLE();
158
0
}
159
/* }}} */
160
161
/* {{{ _php_math_round */
162
/*
163
 * Rounds a number to a certain number of decimal places in a certain rounding
164
 * mode. For the specifics of the algorithm, see http://wiki.php.net/rfc/rounding
165
 */
166
7
PHPAPI double _php_math_round(double value, int places, int mode) {
167
7
  double exponent, tmp_value, tmp_value2;
168
169
7
  if (!zend_finite(value) || value == 0.0) {
170
0
    return value;
171
0
  }
172
173
7
  places = places < INT_MIN+1 ? INT_MIN+1 : places;
174
175
7
  exponent = php_intpow10(abs(places));
176
177
  /**
178
   * When extracting the integer part, the result may be incorrect as a decimal
179
   * number due to floating point errors.
180
   * e.g.
181
   * 0.285 * 10000000000 => 2849999999.9999995
182
   * floor(0.285 * 10000000000) => 2849999999
183
   *
184
   * Add 1 to the absolute value of the value adjusted by floor or ceil, use the
185
   * exponent to return it to its original precision, and compare it with value.
186
   * If it is equal to value, it is assumed that the absolute value is 1 smaller
187
   * due to error and will be corrected.
188
   * e.g.
189
   * 0.285 * 10000000000 => 2849999999.9999995
190
   * floor(0.285 * 10000000000) => 2849999999 (tmp_value)
191
   * tmp_value2 = 2849999999 + 1 => 2850000000
192
   * 2850000000 / 10000000000 == 0.285 => true
193
   * tmp_value = tmp_value2
194
   */
195
196
7
  if (value >= 0.0) {
197
7
    tmp_value = floor(places > 0 ? value * exponent : value / exponent);
198
7
    tmp_value2 = tmp_value + 1.0;
199
7
  } else {
200
0
    tmp_value = ceil(places > 0 ? value * exponent : value / exponent);
201
0
    tmp_value2 = tmp_value - 1.0;
202
0
  }
203
204
7
  if ((places > 0 ? tmp_value2 / exponent : tmp_value2 * exponent) == value) {
205
0
    tmp_value = tmp_value2;
206
0
  }
207
208
  /* This value is beyond our precision, so rounding it is pointless */
209
7
  if (fabs(tmp_value) >= 1e16) {
210
0
    return value;
211
0
  }
212
213
  /* round the temp value */
214
7
  tmp_value = php_round_helper(tmp_value, value, exponent, places, mode);
215
216
  /* see if it makes sense to use simple division to round the value */
217
7
  if (abs(places) < 23) {
218
7
    if (places > 0) {
219
0
      tmp_value = tmp_value / exponent;
220
7
    } else {
221
7
      tmp_value = tmp_value * exponent;
222
7
    }
223
7
  } else {
224
    /* Simple division can't be used since that will cause wrong results.
225
       Instead, the number is converted to a string and back again using
226
       strtod(). strtod() will return the nearest possible FP value for
227
       that string. */
228
229
    /* 40 Bytes should be more than enough for this format string. The
230
       float won't be larger than 1e15 anyway. But just in case, use
231
       snprintf() and make sure the buffer is zero-terminated */
232
0
    char buf[40];
233
0
    snprintf(buf, 39, "%15fe%d", tmp_value, -places);
234
0
    buf[39] = '\0';
235
0
    tmp_value = zend_strtod(buf, NULL);
236
    /* couldn't convert to string and back */
237
0
    if (!zend_finite(tmp_value) || zend_isnan(tmp_value)) {
238
0
      tmp_value = value;
239
0
    }
240
0
  }
241
7
  return tmp_value;
242
7
}
243
/* }}} */
244
245
/* {{{ Return the absolute value of the number */
246
PHP_FUNCTION(abs)
247
0
{
248
0
  zval *value;
249
250
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
251
0
    Z_PARAM_NUMBER(value)
252
0
  ZEND_PARSE_PARAMETERS_END();
253
254
0
  switch (Z_TYPE_P(value)) {
255
0
    case IS_LONG:
256
0
      if (UNEXPECTED(Z_LVAL_P(value) == ZEND_LONG_MIN)) {
257
0
        RETURN_DOUBLE(-(double)ZEND_LONG_MIN);
258
0
      } else {
259
0
        RETURN_LONG(Z_LVAL_P(value) < 0 ? -Z_LVAL_P(value) : Z_LVAL_P(value));
260
0
      }
261
0
    case IS_DOUBLE:
262
0
      RETURN_DOUBLE(fabs(Z_DVAL_P(value)));
263
0
    default: ZEND_UNREACHABLE();
264
0
  }
265
0
}
266
/* }}} */
267
268
/* {{{ Returns the next highest integer value of the number */
269
PHP_FUNCTION(ceil)
270
0
{
271
0
  zval *value;
272
273
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
274
0
    Z_PARAM_NUMBER(value)
275
0
  ZEND_PARSE_PARAMETERS_END();
276
277
0
  switch (Z_TYPE_P(value)) {
278
0
    case IS_LONG:
279
0
      RETURN_DOUBLE(zval_get_double(value));
280
0
    case IS_DOUBLE:
281
0
      RETURN_DOUBLE(ceil(Z_DVAL_P(value)));
282
0
    default: ZEND_UNREACHABLE();
283
0
  }
284
0
}
285
/* }}} */
286
287
/* {{{ Returns the next lowest integer value from the number */
288
PHP_FUNCTION(floor)
289
11
{
290
11
  zval *value;
291
292
33
  ZEND_PARSE_PARAMETERS_START(1, 1)
293
44
    Z_PARAM_NUMBER(value)
294
11
  ZEND_PARSE_PARAMETERS_END();
295
296
11
  switch (Z_TYPE_P(value)) {
297
11
    case IS_LONG:
298
11
      RETURN_DOUBLE(zval_get_double(value));
299
0
    case IS_DOUBLE:
300
0
      RETURN_DOUBLE(floor(Z_DVAL_P(value)));
301
0
    default: ZEND_UNREACHABLE();
302
11
  }
303
11
}
304
/* }}} */
305
306
PHPAPI int php_math_round_mode_from_enum(zend_enum_RoundingMode mode)
307
0
{
308
0
  switch (mode) {
309
0
    case ZEND_ENUM_RoundingMode_HalfAwayFromZero:
310
0
      return PHP_ROUND_HALF_UP;
311
0
    case ZEND_ENUM_RoundingMode_HalfTowardsZero:
312
0
      return PHP_ROUND_HALF_DOWN;
313
0
    case ZEND_ENUM_RoundingMode_HalfEven:
314
0
      return PHP_ROUND_HALF_EVEN;
315
0
    case ZEND_ENUM_RoundingMode_HalfOdd:
316
0
      return PHP_ROUND_HALF_ODD;
317
0
    case ZEND_ENUM_RoundingMode_TowardsZero:
318
0
      return PHP_ROUND_TOWARD_ZERO;
319
0
    case ZEND_ENUM_RoundingMode_AwayFromZero:
320
0
      return PHP_ROUND_AWAY_FROM_ZERO;
321
0
    case ZEND_ENUM_RoundingMode_NegativeInfinity:
322
0
      return PHP_ROUND_FLOOR;
323
0
    case ZEND_ENUM_RoundingMode_PositiveInfinity:
324
0
      return PHP_ROUND_CEILING;
325
0
  }
326
327
0
  ZEND_UNREACHABLE();
328
0
}
329
330
/* {{{ Returns the number rounded to specified precision */
331
PHP_FUNCTION(round)
332
0
{
333
0
  zval *value;
334
0
  int places = 0;
335
0
  zend_long precision = 0;
336
0
  zend_long mode = PHP_ROUND_HALF_UP;
337
0
  zend_object *mode_object = NULL;
338
339
0
  ZEND_PARSE_PARAMETERS_START(1, 3)
340
0
    Z_PARAM_NUMBER(value)
341
0
    Z_PARAM_OPTIONAL
342
0
    Z_PARAM_LONG(precision)
343
0
    Z_PARAM_OBJ_OF_CLASS_OR_LONG(mode_object, rounding_mode_ce, mode)
344
0
  ZEND_PARSE_PARAMETERS_END();
345
346
0
  if (ZEND_NUM_ARGS() >= 2) {
347
0
    if (precision >= 0) {
348
0
      places = ZEND_LONG_INT_OVFL(precision) ? INT_MAX : (int)precision;
349
0
    } else {
350
0
      places = ZEND_LONG_INT_UDFL(precision) ? INT_MIN : (int)precision;
351
0
    }
352
0
  }
353
354
0
  if (mode_object != NULL) {
355
0
    mode = php_math_round_mode_from_enum(zend_enum_fetch_case_id(mode_object));
356
0
  }
357
358
0
  switch (mode) {
359
0
    case PHP_ROUND_HALF_UP:
360
0
    case PHP_ROUND_HALF_DOWN:
361
0
    case PHP_ROUND_HALF_EVEN:
362
0
    case PHP_ROUND_HALF_ODD:
363
0
    case PHP_ROUND_AWAY_FROM_ZERO:
364
0
    case PHP_ROUND_TOWARD_ZERO:
365
0
    case PHP_ROUND_CEILING:
366
0
    case PHP_ROUND_FLOOR:
367
0
      break;
368
0
    default:
369
0
      zend_argument_value_error(3, "must be a valid rounding mode (RoundingMode::*)");
370
0
      RETURN_THROWS();
371
0
  }
372
373
0
  switch (Z_TYPE_P(value)) {
374
0
    case IS_LONG:
375
      /* Simple case - long that doesn't need to be rounded. */
376
0
      if (places >= 0) {
377
0
        RETURN_DOUBLE(zval_get_double(value));
378
0
      }
379
0
      ZEND_FALLTHROUGH;
380
381
0
    case IS_DOUBLE:
382
0
      RETURN_DOUBLE(_php_math_round(zval_get_double(value), (int)places, (int)mode));
383
384
0
    default: ZEND_UNREACHABLE();
385
0
  }
386
0
}
387
/* }}} */
388
389
/* Return the given value if in range of min and max */
390
static void php_math_clamp(zval *return_value, zval *value, zval *min, zval *max)
391
0
{
392
0
  if (Z_TYPE_P(min) == IS_DOUBLE && UNEXPECTED(zend_isnan(Z_DVAL_P(min)))) {
393
0
    zend_argument_value_error(2, "must not be NAN");
394
0
    RETURN_THROWS();
395
0
  }
396
397
0
  if (Z_TYPE_P(max) == IS_DOUBLE && UNEXPECTED(zend_isnan(Z_DVAL_P(max)))) {
398
0
    zend_argument_value_error(3, "must not be NAN");
399
0
    RETURN_THROWS();
400
0
  }
401
402
0
  if (zend_compare(max, min) == -1) {
403
0
    zend_argument_value_error(2, "must be smaller than or equal to argument #3 ($max)");
404
0
    RETURN_THROWS();
405
0
  }
406
407
0
  if (zend_compare(max, value) == -1) {
408
0
    RETURN_COPY(max);
409
0
  }
410
411
0
  if (zend_compare(value, min) == -1) {
412
0
    RETURN_COPY(min);
413
0
  }
414
415
0
  RETURN_COPY(value);
416
0
}
417
418
/* {{{ Return the given value if in range of min and max */
419
PHP_FUNCTION(clamp)
420
1
{
421
1
  zval *zvalue, *zmin, *zmax;
422
423
2
  ZEND_PARSE_PARAMETERS_START(3, 3)
424
2
    Z_PARAM_ZVAL(zvalue)
425
0
    Z_PARAM_ZVAL(zmin)
426
0
    Z_PARAM_ZVAL(zmax)
427
1
  ZEND_PARSE_PARAMETERS_END();
428
429
0
  php_math_clamp(return_value, zvalue, zmin, zmax);
430
0
}
431
/* }}} */
432
433
/* {{{ Return the given value if in range of min and max */
434
ZEND_FRAMELESS_FUNCTION(clamp, 3)
435
0
{
436
0
  zval *zvalue, *zmin, *zmax;
437
0
  Z_FLF_PARAM_ZVAL(1, zvalue);
438
0
  Z_FLF_PARAM_ZVAL(2, zmin);
439
0
  Z_FLF_PARAM_ZVAL(3, zmax);
440
441
0
  php_math_clamp(return_value, zvalue, zmin, zmax);
442
0
}
443
/* }}} */
444
445
/* {{{ Returns the sine of the number in radians */
446
PHP_FUNCTION(sin)
447
18
{
448
18
  double num;
449
450
54
  ZEND_PARSE_PARAMETERS_START(1, 1)
451
72
    Z_PARAM_DOUBLE(num)
452
18
  ZEND_PARSE_PARAMETERS_END();
453
18
  RETURN_DOUBLE(sin(num));
454
18
}
455
/* }}} */
456
457
/* {{{ Returns the cosine of the number in radians */
458
PHP_FUNCTION(cos)
459
27
{
460
27
  double num;
461
462
81
  ZEND_PARSE_PARAMETERS_START(1, 1)
463
108
    Z_PARAM_DOUBLE(num)
464
27
  ZEND_PARSE_PARAMETERS_END();
465
27
  RETURN_DOUBLE(cos(num));
466
27
}
467
/* }}} */
468
469
/* {{{ Returns the tangent of the number in radians */
470
PHP_FUNCTION(tan)
471
14
{
472
14
  double num;
473
474
42
  ZEND_PARSE_PARAMETERS_START(1, 1)
475
56
    Z_PARAM_DOUBLE(num)
476
14
  ZEND_PARSE_PARAMETERS_END();
477
14
  RETURN_DOUBLE(tan(num));
478
14
}
479
/* }}} */
480
481
/* {{{ Returns the arc sine of the number in radians */
482
PHP_FUNCTION(asin)
483
0
{
484
0
  double num;
485
486
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
487
0
    Z_PARAM_DOUBLE(num)
488
0
  ZEND_PARSE_PARAMETERS_END();
489
0
  RETURN_DOUBLE(asin(num));
490
0
}
491
/* }}} */
492
493
/* {{{ Return the arc cosine of the number in radians */
494
PHP_FUNCTION(acos)
495
0
{
496
0
  double num;
497
498
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
499
0
    Z_PARAM_DOUBLE(num)
500
0
  ZEND_PARSE_PARAMETERS_END();
501
0
  RETURN_DOUBLE(acos(num));
502
0
}
503
/* }}} */
504
505
/* {{{ Returns the arc tangent of the number in radians */
506
PHP_FUNCTION(atan)
507
0
{
508
0
  double num;
509
510
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
511
0
    Z_PARAM_DOUBLE(num)
512
0
  ZEND_PARSE_PARAMETERS_END();
513
0
  RETURN_DOUBLE(atan(num));
514
0
}
515
/* }}} */
516
517
/* {{{ Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x */
518
PHP_FUNCTION(atan2)
519
0
{
520
0
  double num1, num2;
521
522
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
523
0
    Z_PARAM_DOUBLE(num1)
524
0
    Z_PARAM_DOUBLE(num2)
525
0
  ZEND_PARSE_PARAMETERS_END();
526
0
  RETURN_DOUBLE(atan2(num1, num2));
527
0
}
528
/* }}} */
529
530
/* {{{ Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2 */
531
PHP_FUNCTION(sinh)
532
0
{
533
0
  double num;
534
535
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
536
0
    Z_PARAM_DOUBLE(num)
537
0
  ZEND_PARSE_PARAMETERS_END();
538
0
  RETURN_DOUBLE(sinh(num));
539
0
}
540
/* }}} */
541
542
/* {{{ Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2 */
543
PHP_FUNCTION(cosh)
544
0
{
545
0
  double num;
546
547
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
548
0
    Z_PARAM_DOUBLE(num)
549
0
  ZEND_PARSE_PARAMETERS_END();
550
0
  RETURN_DOUBLE(cosh(num));
551
0
}
552
/* }}} */
553
554
/* {{{ Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number) */
555
PHP_FUNCTION(tanh)
556
0
{
557
0
  double num;
558
559
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
560
0
    Z_PARAM_DOUBLE(num)
561
0
  ZEND_PARSE_PARAMETERS_END();
562
0
  RETURN_DOUBLE(tanh(num));
563
0
}
564
/* }}} */
565
566
/* {{{ Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number */
567
PHP_FUNCTION(asinh)
568
0
{
569
0
  double num;
570
571
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
572
0
    Z_PARAM_DOUBLE(num)
573
0
  ZEND_PARSE_PARAMETERS_END();
574
0
  RETURN_DOUBLE(asinh(num));
575
0
}
576
/* }}} */
577
578
/* {{{ Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number */
579
PHP_FUNCTION(acosh)
580
0
{
581
0
  double num;
582
583
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
584
0
    Z_PARAM_DOUBLE(num)
585
0
  ZEND_PARSE_PARAMETERS_END();
586
0
  RETURN_DOUBLE(acosh(num));
587
0
}
588
/* }}} */
589
590
/* {{{ Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number */
591
PHP_FUNCTION(atanh)
592
0
{
593
0
  double num;
594
595
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
596
0
    Z_PARAM_DOUBLE(num)
597
0
  ZEND_PARSE_PARAMETERS_END();
598
0
  RETURN_DOUBLE(atanh(num));
599
0
}
600
/* }}} */
601
602
/* {{{ Returns an approximation of pi */
603
PHP_FUNCTION(pi)
604
0
{
605
0
  ZEND_PARSE_PARAMETERS_NONE();
606
607
0
  RETURN_DOUBLE(M_PI);
608
0
}
609
/* }}} */
610
611
/* {{{ Returns whether argument is finite */
612
PHP_FUNCTION(is_finite)
613
11
{
614
11
  double dval;
615
616
33
  ZEND_PARSE_PARAMETERS_START(1, 1)
617
44
    Z_PARAM_DOUBLE(dval)
618
11
  ZEND_PARSE_PARAMETERS_END();
619
11
  RETURN_BOOL(zend_finite(dval));
620
11
}
621
/* }}} */
622
623
/* {{{ Returns whether argument is infinite */
624
PHP_FUNCTION(is_infinite)
625
0
{
626
0
  double dval;
627
628
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
629
0
    Z_PARAM_DOUBLE(dval)
630
0
  ZEND_PARSE_PARAMETERS_END();
631
0
  RETURN_BOOL(zend_isinf(dval));
632
0
}
633
/* }}} */
634
635
/* {{{ Returns whether argument is not a number */
636
PHP_FUNCTION(is_nan)
637
226
{
638
226
  double dval;
639
640
678
  ZEND_PARSE_PARAMETERS_START(1, 1)
641
904
    Z_PARAM_DOUBLE(dval)
642
226
  ZEND_PARSE_PARAMETERS_END();
643
226
  RETURN_BOOL(zend_isnan(dval));
644
226
}
645
/* }}} */
646
647
/* {{{ Returns base raised to the power of exponent. Returns integer result when possible */
648
PHP_FUNCTION(pow)
649
0
{
650
0
  zval *zbase, *zexp;
651
652
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
653
0
    Z_PARAM_ZVAL(zbase)
654
0
    Z_PARAM_ZVAL(zexp)
655
0
  ZEND_PARSE_PARAMETERS_END();
656
657
0
  pow_function(return_value, zbase, zexp);
658
0
}
659
/* }}} */
660
661
/* {{{ Returns e raised to the power of the number */
662
PHP_FUNCTION(exp)
663
0
{
664
0
  double num;
665
666
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
667
0
    Z_PARAM_DOUBLE(num)
668
0
  ZEND_PARSE_PARAMETERS_END();
669
670
0
  RETURN_DOUBLE(exp(num));
671
0
}
672
/* }}} */
673
674
/* {{{ Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
675
PHP_FUNCTION(expm1)
676
0
{
677
0
  double num;
678
679
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
680
0
    Z_PARAM_DOUBLE(num)
681
0
  ZEND_PARSE_PARAMETERS_END();
682
683
0
  RETURN_DOUBLE(expm1(num));
684
0
}
685
/* }}} */
686
687
/* {{{ Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */
688
PHP_FUNCTION(log1p)
689
0
{
690
0
  double num;
691
692
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
693
0
    Z_PARAM_DOUBLE(num)
694
0
  ZEND_PARSE_PARAMETERS_END();
695
696
0
  RETURN_DOUBLE(log1p(num));
697
0
}
698
/* }}} */
699
700
/* {{{ Returns the natural logarithm of the number, or the base log if base is specified */
701
PHP_FUNCTION(log)
702
24
{
703
24
  double num, base = 0;
704
705
71
  ZEND_PARSE_PARAMETERS_START(1, 2)
706
92
    Z_PARAM_DOUBLE(num)
707
22
    Z_PARAM_OPTIONAL
708
50
    Z_PARAM_DOUBLE(base)
709
24
  ZEND_PARSE_PARAMETERS_END();
710
711
22
  if (ZEND_NUM_ARGS() == 1) {
712
19
    RETURN_DOUBLE(log(num));
713
19
  }
714
715
3
  if (base == 2.0) {
716
0
    RETURN_DOUBLE(log2(num));
717
0
  }
718
719
3
  if (base == 10.0) {
720
0
    RETURN_DOUBLE(log10(num));
721
0
  }
722
723
3
  if (base == 1.0) {
724
0
    RETURN_DOUBLE(ZEND_NAN);
725
0
  }
726
727
3
  if (base <= 0.0) {
728
3
    zend_argument_value_error(2, "must be greater than 0");
729
3
    RETURN_THROWS();
730
3
  }
731
732
0
  RETURN_DOUBLE(log(num) / log(base));
733
0
}
734
/* }}} */
735
736
/* {{{ Returns the base-10 logarithm of the number */
737
PHP_FUNCTION(log10)
738
44
{
739
44
  double num;
740
741
132
  ZEND_PARSE_PARAMETERS_START(1, 1)
742
176
    Z_PARAM_DOUBLE(num)
743
44
  ZEND_PARSE_PARAMETERS_END();
744
745
44
  RETURN_DOUBLE(log10(num));
746
44
}
747
/* }}} */
748
749
/* {{{ Returns the square root of the number */
750
PHP_FUNCTION(sqrt)
751
33
{
752
33
  double num;
753
754
99
  ZEND_PARSE_PARAMETERS_START(1, 1)
755
132
    Z_PARAM_DOUBLE(num)
756
33
  ZEND_PARSE_PARAMETERS_END();
757
758
33
  RETURN_DOUBLE(sqrt(num));
759
33
}
760
/* }}} */
761
762
/* {{{ Returns sqrt(num1*num1 + num2*num2) */
763
PHP_FUNCTION(hypot)
764
0
{
765
0
  double num1, num2;
766
767
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
768
0
    Z_PARAM_DOUBLE(num1)
769
0
    Z_PARAM_DOUBLE(num2)
770
0
  ZEND_PARSE_PARAMETERS_END();
771
772
0
  RETURN_DOUBLE(hypot(num1, num2));
773
0
}
774
/* }}} */
775
776
/* {{{ Converts the number in degrees to the radian equivalent */
777
PHP_FUNCTION(deg2rad)
778
283
{
779
283
  double deg;
780
781
849
  ZEND_PARSE_PARAMETERS_START(1, 1)
782
1.13k
    Z_PARAM_DOUBLE(deg)
783
283
  ZEND_PARSE_PARAMETERS_END();
784
283
  RETURN_DOUBLE((deg / 180.0) * M_PI);
785
283
}
786
/* }}} */
787
788
/* {{{ Converts the radian number to the equivalent number in degrees */
789
PHP_FUNCTION(rad2deg)
790
0
{
791
0
  double rad;
792
793
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
794
0
    Z_PARAM_DOUBLE(rad)
795
0
  ZEND_PARSE_PARAMETERS_END();
796
797
0
  RETURN_DOUBLE((rad / M_PI) * 180);
798
0
}
799
/* }}} */
800
801
/* {{{ _php_math_basetolong */
802
/*
803
 * Convert a string representation of a base(2-36) number to a long.
804
 */
805
PHPAPI zend_long _php_math_basetolong(zval *arg, int base)
806
0
{
807
0
  zend_long num = 0, digit, onum;
808
0
  zend_long i;
809
0
  char c, *s;
810
811
0
  if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
812
0
    return 0;
813
0
  }
814
815
0
  s = Z_STRVAL_P(arg);
816
817
0
  for (i = Z_STRLEN_P(arg); i > 0; i--) {
818
0
    c = *s++;
819
820
0
    digit = (c >= '0' && c <= '9') ? c - '0'
821
0
      : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
822
0
      : (c >= 'a' && c <= 'z') ? c - 'a' + 10
823
0
      : base;
824
825
0
    if (digit >= base) {
826
0
      continue;
827
0
    }
828
829
0
    onum = num;
830
0
    num = num * base + digit;
831
0
    if (num > onum)
832
0
      continue;
833
834
0
    {
835
836
0
      php_error_docref(NULL, E_WARNING, "Number %s is too big to fit in long", s);
837
0
      return ZEND_LONG_MAX;
838
0
    }
839
0
  }
840
841
0
  return num;
842
0
}
843
/* }}} */
844
845
/* {{{ _php_math_basetozval */
846
/*
847
 * Convert a string representation of a base(2-36) number to a zval.
848
 */
849
PHPAPI void _php_math_basetozval(zend_string *str, int base, zval *ret)
850
28
{
851
28
  zend_long num = 0;
852
28
  double fnum = 0;
853
28
  int mode = 0;
854
28
  char c, *s, *e;
855
28
  zend_long cutoff;
856
28
  int cutlim;
857
28
  int invalidchars = 0;
858
859
28
  s = ZSTR_VAL(str);
860
28
  e = s + ZSTR_LEN(str);
861
862
  /* Skip leading whitespace */
863
28
  while (s < e && isspace((unsigned char)*s)) s++;
864
  /* Skip trailing whitespace */
865
28
  while (s < e && isspace((unsigned char)e[-1])) e--;
866
867
28
  if (e - s >= 2) {
868
26
    if (base == 16 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) s += 2;
869
26
    if (base == 8 && s[0] == '0' && (s[1] == 'o' || s[1] == 'O')) s += 2;
870
26
    if (base == 2 && s[0] == '0' && (s[1] == 'b' || s[1] == 'B')) s += 2;
871
26
  }
872
873
28
  cutoff = ZEND_LONG_MAX / base;
874
28
  cutlim = ZEND_LONG_MAX % base;
875
876
1.69k
  while (s < e) {
877
1.66k
    c = *s++;
878
879
    /* might not work for EBCDIC */
880
1.66k
    if (c >= '0' && c <= '9')
881
1.66k
      c -= '0';
882
0
    else if (c >= 'A' && c <= 'Z')
883
0
      c -= 'A' - 10;
884
0
    else if (c >= 'a' && c <= 'z')
885
0
      c -= 'a' - 10;
886
0
    else {
887
0
      invalidchars++;
888
0
      continue;
889
0
    }
890
891
1.66k
    if (c >= base) {
892
0
      invalidchars++;
893
0
      continue;
894
0
    }
895
896
1.66k
    switch (mode) {
897
1.65k
    case 0: /* Integer */
898
1.65k
      if (num < cutoff || (num == cutoff && c <= cutlim)) {
899
1.63k
        num = num * base + c;
900
1.63k
        break;
901
1.63k
      } else {
902
17
        fnum = (double)num;
903
17
        mode = 1;
904
17
      }
905
17
      ZEND_FALLTHROUGH;
906
34
    case 1: /* Float */
907
34
      fnum = fnum * base + c;
908
1.66k
    }
909
1.66k
  }
910
911
28
  if (invalidchars > 0) {
912
0
    zend_error(E_DEPRECATED, "Invalid characters passed for attempted conversion, these have been ignored");
913
0
  }
914
915
28
  if (mode == 1) {
916
17
    ZVAL_DOUBLE(ret, fnum);
917
17
  } else {
918
11
    ZVAL_LONG(ret, num);
919
11
  }
920
28
}
921
/* }}} */
922
923
/* {{{ _php_math_longtobase */
924
/*
925
 * Convert a long to a string containing a base(2-36) representation of
926
 * the number.
927
 */
928
PHPAPI zend_string * _php_math_longtobase(zend_long arg, int base)
929
11
{
930
11
  static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
931
11
  char buf[(sizeof(zend_ulong) << 3) + 1];
932
11
  char *ptr, *end;
933
11
  zend_ulong value;
934
935
11
  if (base < 2 || base > 36) {
936
0
    return ZSTR_EMPTY_ALLOC();
937
0
  }
938
939
11
  value = arg;
940
941
11
  end = ptr = buf + sizeof(buf) - 1;
942
11
  *ptr = '\0';
943
944
62
  do {
945
62
    ZEND_ASSERT(ptr > buf);
946
62
    *--ptr = digits[value % base];
947
62
    value /= base;
948
62
  } while (value);
949
950
11
  return zend_string_init(ptr, end - ptr, 0);
951
11
}
952
/* }}} */
953
954
/* {{{ _php_math_longtobase_pwr2 */
955
/*
956
 * Convert a long to a string containing a base(2,4,6,16,32) representation of
957
 * the number.
958
 */
959
static zend_always_inline zend_string * _php_math_longtobase_pwr2(zend_long arg, int base_log2)
960
0
{
961
0
  static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
962
0
  zend_ulong value;
963
0
  size_t len;
964
0
  zend_string *ret;
965
0
  char *ptr;
966
967
0
  value = arg;
968
969
0
  if (value == 0) {
970
0
    len = 1;
971
0
  } else {
972
0
    len = ((sizeof(value) * 8 - zend_ulong_nlz(value)) + (base_log2 - 1)) / base_log2;
973
0
  }
974
975
0
  ret = zend_string_alloc(len, 0);
976
0
  ptr = ZSTR_VAL(ret) + len;
977
0
  *ptr = '\0';
978
979
0
  do {
980
0
    ZEND_ASSERT(ptr > ZSTR_VAL(ret));
981
0
    *--ptr = digits[value & ((1 << base_log2) - 1)];
982
0
    value >>= base_log2;
983
0
  } while (value);
984
985
0
  return ret;
986
0
}
987
/* }}} */
988
989
/* {{{ _php_math_zvaltobase */
990
/*
991
 * Convert a zval to a string containing a base(2-36) representation of
992
 * the number.
993
 */
994
PHPAPI zend_string * _php_math_zvaltobase(zval *arg, int base)
995
28
{
996
28
  static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
997
998
28
  if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
999
0
    return ZSTR_EMPTY_ALLOC();
1000
0
  }
1001
1002
28
  if (Z_TYPE_P(arg) == IS_DOUBLE) {
1003
17
    double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
1004
17
    char *ptr, *end;
1005
17
    char buf[ZEND_DOUBLE_MAX_LENGTH];
1006
1007
    /* Don't try to convert +/- infinity */
1008
17
    if (fvalue == ZEND_INFINITY || fvalue == -ZEND_INFINITY) {
1009
0
      zend_value_error("An infinite value cannot be converted to base %d", base);
1010
0
      return NULL;
1011
0
    }
1012
1013
17
    end = ptr = buf + sizeof(buf) - 1;
1014
17
    *ptr = '\0';
1015
1016
340
    do {
1017
340
      *--ptr = digits[(int) fmod(fvalue, base)];
1018
340
      fvalue /= base;
1019
340
    } while (ptr > buf && fabs(fvalue) >= 1);
1020
1021
17
    return zend_string_init(ptr, end - ptr, 0);
1022
17
  }
1023
1024
11
  return _php_math_longtobase(Z_LVAL_P(arg), base);
1025
28
}
1026
/* }}} */
1027
1028
/* {{{ Returns the decimal equivalent of the binary number */
1029
PHP_FUNCTION(bindec)
1030
0
{
1031
0
  zend_string *arg;
1032
1033
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
1034
0
    Z_PARAM_STR(arg)
1035
0
  ZEND_PARSE_PARAMETERS_END();
1036
1037
0
  _php_math_basetozval(arg, 2, return_value);
1038
0
}
1039
/* }}} */
1040
1041
/* {{{ Returns the decimal equivalent of the hexadecimal number */
1042
PHP_FUNCTION(hexdec)
1043
0
{
1044
0
  zend_string *arg;
1045
1046
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
1047
0
    Z_PARAM_STR(arg)
1048
0
  ZEND_PARSE_PARAMETERS_END();
1049
1050
0
  _php_math_basetozval(arg, 16, return_value);
1051
0
}
1052
/* }}} */
1053
1054
/* {{{ Returns the decimal equivalent of an octal string */
1055
PHP_FUNCTION(octdec)
1056
0
{
1057
0
  zend_string *arg;
1058
1059
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
1060
0
    Z_PARAM_STR(arg)
1061
0
  ZEND_PARSE_PARAMETERS_END();
1062
1063
0
  _php_math_basetozval(arg, 8, return_value);
1064
0
}
1065
/* }}} */
1066
1067
/* {{{ Returns a string containing a binary representation of the number */
1068
PHP_FUNCTION(decbin)
1069
0
{
1070
0
  zend_long arg;
1071
1072
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
1073
0
    Z_PARAM_LONG(arg)
1074
0
  ZEND_PARSE_PARAMETERS_END();
1075
1076
0
  RETURN_STR(_php_math_longtobase_pwr2(arg, 1));
1077
0
}
1078
/* }}} */
1079
1080
/* {{{ Returns a string containing an octal representation of the given number */
1081
PHP_FUNCTION(decoct)
1082
0
{
1083
0
  zend_long arg;
1084
1085
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
1086
0
    Z_PARAM_LONG(arg)
1087
0
  ZEND_PARSE_PARAMETERS_END();
1088
1089
0
  RETURN_STR(_php_math_longtobase_pwr2(arg, 3));
1090
0
}
1091
/* }}} */
1092
1093
/* {{{ Returns a string containing a hexadecimal representation of the given number */
1094
PHP_FUNCTION(dechex)
1095
0
{
1096
0
  zend_long arg;
1097
1098
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
1099
0
    Z_PARAM_LONG(arg)
1100
0
  ZEND_PARSE_PARAMETERS_END();
1101
1102
0
  RETURN_STR(_php_math_longtobase_pwr2(arg, 4));
1103
0
}
1104
/* }}} */
1105
1106
ZEND_FRAMELESS_FUNCTION(dechex, 1)
1107
0
{
1108
0
  zend_long arg;
1109
1110
0
  Z_FLF_PARAM_LONG(1, arg);
1111
1112
0
  RETVAL_STR(_php_math_longtobase_pwr2(arg, 4));
1113
1114
0
flf_clean:;
1115
0
}
1116
1117
/* {{{ Converts a number in a string from any base <= 36 to any base <= 36 */
1118
PHP_FUNCTION(base_convert)
1119
28
{
1120
28
  zval temp;
1121
28
  zend_string *number;
1122
28
  zend_long frombase, tobase;
1123
28
  zend_string *result;
1124
1125
84
  ZEND_PARSE_PARAMETERS_START(3, 3)
1126
112
    Z_PARAM_STR(number)
1127
140
    Z_PARAM_LONG(frombase)
1128
140
    Z_PARAM_LONG(tobase)
1129
28
  ZEND_PARSE_PARAMETERS_END();
1130
1131
28
  if (frombase < 2 || frombase > 36) {
1132
0
    zend_argument_value_error(2, "must be between 2 and 36 (inclusive)");
1133
0
    RETURN_THROWS();
1134
0
  }
1135
28
  if (tobase < 2 || tobase > 36) {
1136
0
    zend_argument_value_error(3, "must be between 2 and 36 (inclusive)");
1137
0
    RETURN_THROWS();
1138
0
  }
1139
1140
28
  _php_math_basetozval(number, (int)frombase, &temp);
1141
28
  result = _php_math_zvaltobase(&temp, (int)tobase);
1142
28
  if (!result) {
1143
0
    RETURN_THROWS();
1144
0
  }
1145
1146
28
  RETVAL_STR(result);
1147
28
}
1148
/* }}} */
1149
1150
/* {{{ _php_math_number_format */
1151
PHPAPI zend_string *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
1152
0
{
1153
0
  return _php_math_number_format_ex(d, dec, &dec_point, 1, &thousand_sep, 1);
1154
0
}
1155
1156
PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, const char *dec_point,
1157
    size_t dec_point_len, const char *thousand_sep, size_t thousand_sep_len)
1158
0
{
1159
0
  zend_string *res;
1160
0
  zend_string *tmpbuf;
1161
0
  char *s, *t;  /* source, target */
1162
0
  char *dp;
1163
0
  size_t integral;
1164
0
  size_t reslen = 0;
1165
0
  int count = 0;
1166
0
  int is_negative = 0;
1167
1168
0
  if (d < 0) {
1169
0
    is_negative = 1;
1170
0
    d = -d;
1171
0
  }
1172
1173
0
  d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
1174
0
  dec = MAX(0, dec);
1175
0
  tmpbuf = strpprintf(0, "%.*F", dec, d);
1176
0
  if (tmpbuf == NULL) {
1177
0
    return NULL;
1178
0
  } else if (!isdigit((unsigned char)ZSTR_VAL(tmpbuf)[0])) {
1179
0
    return tmpbuf;
1180
0
  }
1181
1182
  /* Check if the number is no longer negative after rounding */
1183
0
  if (is_negative && d == 0) {
1184
0
    is_negative = 0;
1185
0
  }
1186
1187
  /* find decimal point, if expected */
1188
0
  if (dec) {
1189
0
    dp = strpbrk(ZSTR_VAL(tmpbuf), ".,");
1190
0
  } else {
1191
0
    dp = NULL;
1192
0
  }
1193
1194
  /* calculate the length of the return buffer */
1195
0
  if (dp) {
1196
0
    integral = (dp - ZSTR_VAL(tmpbuf));
1197
0
  } else {
1198
    /* no decimal point was found */
1199
0
    integral = ZSTR_LEN(tmpbuf);
1200
0
  }
1201
1202
  /* allow for thousand separators */
1203
0
  if (thousand_sep) {
1204
0
    integral = zend_safe_addmult((integral-1)/3, thousand_sep_len, integral, "number formatting");
1205
0
  }
1206
1207
0
  reslen = integral;
1208
1209
0
  if (dec) {
1210
0
    reslen += dec;
1211
1212
0
    if (dec_point) {
1213
0
      reslen = zend_safe_addmult(reslen, 1, dec_point_len, "number formatting");
1214
0
    }
1215
0
  }
1216
1217
  /* add a byte for minus sign */
1218
0
  if (is_negative) {
1219
0
    reslen++;
1220
0
  }
1221
0
  res = zend_string_alloc(reslen, 0);
1222
1223
0
  s = ZSTR_VAL(tmpbuf) + ZSTR_LEN(tmpbuf) - 1;
1224
0
  t = ZSTR_VAL(res) + reslen;
1225
0
  *t-- = '\0';
1226
1227
  /* copy the decimal places.
1228
   * Take care, as the sprintf implementation may return less places than
1229
   * we requested due to internal buffer limitations */
1230
0
  if (dec) {
1231
0
    size_t declen = (dp ? s - dp : 0);
1232
0
    size_t topad = (size_t)dec > declen ? dec - declen : 0;
1233
1234
    /* pad with '0's */
1235
0
    while (topad--) {
1236
0
      *t-- = '0';
1237
0
    }
1238
1239
0
    if (dp) {
1240
0
      s -= declen + 1; /* +1 to skip the point */
1241
0
      t -= declen;
1242
1243
      /* now copy the chars after the point */
1244
0
      memcpy(t + 1, dp + 1, declen);
1245
0
    }
1246
1247
    /* add decimal point */
1248
0
    if (dec_point) {
1249
0
      t -= dec_point_len;
1250
0
      memcpy(t + 1, dec_point, dec_point_len);
1251
0
    }
1252
0
  }
1253
1254
  /* copy the numbers before the decimal point, adding thousand
1255
   * separator every three digits */
1256
0
  while (s >= ZSTR_VAL(tmpbuf)) {
1257
0
    *t-- = *s--;
1258
0
    if (thousand_sep && (++count%3)==0 && s >= ZSTR_VAL(tmpbuf)) {
1259
0
      t -= thousand_sep_len;
1260
0
      memcpy(t + 1, thousand_sep, thousand_sep_len);
1261
0
    }
1262
0
  }
1263
1264
  /* and a minus sign, if needed */
1265
0
  if (is_negative) {
1266
0
    *t-- = '-';
1267
0
  }
1268
1269
0
  ZSTR_LEN(res) = reslen;
1270
0
  zend_string_release_ex(tmpbuf, 0);
1271
0
  return res;
1272
0
}
1273
1274
PHPAPI zend_string *_php_math_number_format_long(zend_long num, zend_long dec, const char *dec_point,
1275
    size_t dec_point_len, const char *thousand_sep, size_t thousand_sep_len)
1276
0
{
1277
0
  static const zend_ulong powers[] = {
1278
0
    1, 10, 100, 1000, 10000,
1279
0
    100000, 1000000, 10000000, 100000000, 1000000000,
1280
0
#if SIZEOF_ZEND_LONG == 8
1281
0
    10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000,
1282
0
    1000000000000000, 10000000000000000, 100000000000000000, 1000000000000000000, 10000000000000000000ul
1283
#elif SIZEOF_ZEND_LONG > 8
1284
# error "Unknown SIZEOF_ZEND_LONG"
1285
#endif
1286
0
  };
1287
1288
0
  int is_negative = 0;
1289
0
  zend_ulong tmpnum;
1290
0
  zend_ulong power;
1291
0
  zend_ulong power_half;
1292
0
  zend_ulong rest;
1293
1294
0
  zend_string *tmpbuf;
1295
0
  zend_string *res;
1296
0
  size_t reslen;
1297
0
  char *s, *t;  /* source, target */
1298
0
  int count = 0;
1299
0
  size_t topad;
1300
1301
  // unsigned absolute number and memorize negative sign
1302
0
  if (num < 0) {
1303
0
    is_negative = 1;
1304
0
    tmpnum = ((zend_ulong)-(num + 1)) + 1;
1305
0
  } else {
1306
0
    tmpnum = (zend_ulong)num;
1307
0
  }
1308
1309
  // rounding the number
1310
0
  if (dec < 0) {
1311
    // Check rounding to more negative places than possible
1312
0
    if (dec < -(sizeof(powers) / sizeof(powers[0]) - 1)) {
1313
0
      tmpnum = 0;
1314
0
    } else {
1315
0
      power = powers[-dec];
1316
0
      power_half = power / 2;
1317
0
      rest = tmpnum % power;
1318
0
      tmpnum = tmpnum / power;
1319
1320
0
      if (rest >= power_half) {
1321
0
        tmpnum = tmpnum * power + power;
1322
0
      } else {
1323
0
        tmpnum = tmpnum * power;
1324
0
      }
1325
0
    }
1326
1327
    // prevent resulting in negative zero
1328
0
    if (tmpnum == 0) {
1329
0
      is_negative = 0;
1330
0
    }
1331
0
  }
1332
1333
0
  tmpbuf = strpprintf(0, ZEND_ULONG_FMT, tmpnum);
1334
0
  reslen = ZSTR_LEN(tmpbuf);
1335
1336
  /* allow for thousand separators */
1337
0
  if (thousand_sep) {
1338
0
    reslen = zend_safe_addmult((reslen-1)/3, thousand_sep_len, reslen, "number formatting");
1339
0
  }
1340
1341
0
  reslen += is_negative;
1342
1343
0
  if (dec > 0) {
1344
0
    reslen += dec;
1345
1346
0
    if (dec_point) {
1347
0
      reslen = zend_safe_addmult(reslen, 1, dec_point_len, "number formatting");
1348
0
    }
1349
0
  }
1350
1351
0
  res = zend_string_alloc(reslen, 0);
1352
1353
0
  s = ZSTR_VAL(tmpbuf) + ZSTR_LEN(tmpbuf) - 1;
1354
0
  t = ZSTR_VAL(res) + reslen;
1355
0
  *t-- = '\0';
1356
1357
  /* copy the decimal places. */
1358
0
  if (dec > 0) {
1359
0
    topad = (size_t)dec;
1360
1361
    /* pad with '0's */
1362
0
    while (topad--) {
1363
0
      *t-- = '0';
1364
0
    }
1365
1366
    /* add decimal point */
1367
0
    if (dec_point) {
1368
0
      t -= dec_point_len;
1369
0
      memcpy(t + 1, dec_point, dec_point_len);
1370
0
    }
1371
0
  }
1372
1373
  /* copy the numbers before the decimal point, adding thousand
1374
   * separator every three digits */
1375
0
  while (s >= ZSTR_VAL(tmpbuf)) {
1376
0
    *t-- = *s--;
1377
0
    if (thousand_sep && (++count % 3) == 0 && s >= ZSTR_VAL(tmpbuf)) {
1378
0
      t -= thousand_sep_len;
1379
0
      memcpy(t + 1, thousand_sep, thousand_sep_len);
1380
0
    }
1381
0
  }
1382
1383
0
  if (is_negative) {
1384
0
    *t-- = '-';
1385
0
  }
1386
1387
0
  ZSTR_LEN(res) = reslen;
1388
0
  zend_string_release_ex(tmpbuf, 0);
1389
0
  return res;
1390
0
}
1391
1392
/* {{{ Formats a number with grouped thousands */
1393
PHP_FUNCTION(number_format)
1394
0
{
1395
0
  zval* num;
1396
0
  zend_long dec = 0;
1397
0
  int dec_int;
1398
0
  char *thousand_sep = NULL, *dec_point = NULL;
1399
0
  size_t thousand_sep_len = 0, dec_point_len = 0;
1400
1401
0
  ZEND_PARSE_PARAMETERS_START(1, 4)
1402
0
    Z_PARAM_NUMBER(num)
1403
0
    Z_PARAM_OPTIONAL
1404
0
    Z_PARAM_LONG(dec)
1405
0
    Z_PARAM_STRING_OR_NULL(dec_point, dec_point_len)
1406
0
    Z_PARAM_STRING_OR_NULL(thousand_sep, thousand_sep_len)
1407
0
  ZEND_PARSE_PARAMETERS_END();
1408
1409
0
  if (dec_point == NULL) {
1410
0
    dec_point = ".";
1411
0
    dec_point_len = 1;
1412
0
  }
1413
0
  if (thousand_sep == NULL) {
1414
0
    thousand_sep = ",";
1415
0
    thousand_sep_len = 1;
1416
0
  }
1417
1418
0
  if (UNEXPECTED(dec > INT_MAX || dec < INT_MIN)) {
1419
0
    zend_argument_value_error(2, "must be between %d and %d", INT_MIN, INT_MAX);
1420
0
    RETURN_THROWS();
1421
0
  }
1422
1423
0
  switch (Z_TYPE_P(num)) {
1424
0
    case IS_LONG:
1425
0
      RETURN_STR(_php_math_number_format_long(Z_LVAL_P(num), dec, dec_point, dec_point_len, thousand_sep, thousand_sep_len));
1426
1427
0
    case IS_DOUBLE:
1428
      // double values of >= 2^52 can not have fractional digits anymore
1429
      // Casting to long on 64bit will not loose precision on rounding
1430
0
      if (UNEXPECTED(
1431
0
        (Z_DVAL_P(num) >= 4503599627370496.0 || Z_DVAL_P(num) <= -4503599627370496.0)
1432
0
        && ZEND_DOUBLE_FITS_LONG(Z_DVAL_P(num))
1433
0
      )) {
1434
0
        RETURN_STR(_php_math_number_format_long((zend_long)Z_DVAL_P(num), dec, dec_point, dec_point_len, thousand_sep, thousand_sep_len));
1435
0
      }
1436
1437
0
      dec_int = (int) dec;
1438
0
      RETURN_STR(_php_math_number_format_ex(Z_DVAL_P(num), dec_int, dec_point, dec_point_len, thousand_sep, thousand_sep_len));
1439
1440
0
    default: ZEND_UNREACHABLE();
1441
0
  }
1442
0
}
1443
/* }}} */
1444
1445
/* {{{ Returns the remainder of dividing x by y as a float */
1446
PHP_FUNCTION(fmod)
1447
0
{
1448
0
  double num1, num2;
1449
1450
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
1451
0
    Z_PARAM_DOUBLE(num1)
1452
0
    Z_PARAM_DOUBLE(num2)
1453
0
  ZEND_PARSE_PARAMETERS_END();
1454
1455
0
  RETURN_DOUBLE(fmod(num1, num2));
1456
0
}
1457
/* }}} */
1458
1459
/* {{{ Perform floating-point division of dividend / divisor
1460
   with IEEE-754 semantics for division by zero. */
1461
#ifdef __clang__
1462
__attribute__((no_sanitize("float-divide-by-zero")))
1463
#endif
1464
PHP_FUNCTION(fdiv)
1465
226
{
1466
226
  double dividend, divisor;
1467
1468
676
  ZEND_PARSE_PARAMETERS_START(2, 2)
1469
896
    Z_PARAM_DOUBLE(dividend)
1470
1.12k
    Z_PARAM_DOUBLE(divisor)
1471
226
  ZEND_PARSE_PARAMETERS_END();
1472
1473
224
  RETURN_DOUBLE(dividend / divisor);
1474
224
}
1475
/* }}} */
1476
1477
/* {{{ Perform floating-point exponentiation with IEEE-754 semantics. */
1478
PHP_FUNCTION(fpow)
1479
0
{
1480
0
  double base, exponent;
1481
1482
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
1483
0
    Z_PARAM_DOUBLE(base)
1484
0
    Z_PARAM_DOUBLE(exponent)
1485
0
  ZEND_PARSE_PARAMETERS_END();
1486
1487
0
  RETURN_DOUBLE(pow(base, exponent));
1488
0
}
1489
/* }}} */
1490
1491
/* {{{ Returns the integer quotient of the division of dividend by divisor */
1492
PHP_FUNCTION(intdiv)
1493
754
{
1494
754
  zend_long dividend, divisor;
1495
1496
2.26k
  ZEND_PARSE_PARAMETERS_START(2, 2)
1497
3.01k
    Z_PARAM_LONG(dividend)
1498
3.77k
    Z_PARAM_LONG(divisor)
1499
754
  ZEND_PARSE_PARAMETERS_END();
1500
1501
751
  if (divisor == 0) {
1502
0
    zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
1503
0
    RETURN_THROWS();
1504
751
  } else if (divisor == -1 && dividend == ZEND_LONG_MIN) {
1505
    /* Prevent overflow error/crash ... really should not happen:
1506
       We don't return a float here as that violates function contract */
1507
0
    zend_throw_exception_ex(zend_ce_arithmetic_error, 0, "Division of PHP_INT_MIN by -1 is not an integer");
1508
0
    RETURN_THROWS();
1509
0
  }
1510
1511
751
  RETURN_LONG(dividend / divisor);
1512
751
}
1513
/* }}} */