Coverage Report

Created: 2025-12-31 07:28

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