Coverage Report

Created: 2026-06-02 06:36

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