Coverage Report

Created: 2025-06-13 06:43

/src/php-src/ext/standard/math.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright (c) The PHP Group                                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to version 3.01 of the PHP license,      |
6
   | that is bundled with this package in the file LICENSE, and is        |
7
   | available through the world-wide-web at the following url:           |
8
   | 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
5
{
291
5
  zval *value;
292
293
15
  ZEND_PARSE_PARAMETERS_START(1, 1)
294
20
    Z_PARAM_NUMBER(value)
295
5
  ZEND_PARSE_PARAMETERS_END();
296
297
5
  switch (Z_TYPE_P(value)) {
298
5
    case IS_LONG:
299
5
      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
5
  }
304
5
}
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
/* {{{ Returns the sine of the number in radians */
393
PHP_FUNCTION(sin)
394
15
{
395
15
  double num;
396
397
45
  ZEND_PARSE_PARAMETERS_START(1, 1)
398
60
    Z_PARAM_DOUBLE(num)
399
15
  ZEND_PARSE_PARAMETERS_END();
400
15
  RETURN_DOUBLE(sin(num));
401
15
}
402
/* }}} */
403
404
/* {{{ Returns the cosine of the number in radians */
405
PHP_FUNCTION(cos)
406
21
{
407
21
  double num;
408
409
63
  ZEND_PARSE_PARAMETERS_START(1, 1)
410
84
    Z_PARAM_DOUBLE(num)
411
21
  ZEND_PARSE_PARAMETERS_END();
412
21
  RETURN_DOUBLE(cos(num));
413
21
}
414
/* }}} */
415
416
/* {{{ Returns the tangent of the number in radians */
417
PHP_FUNCTION(tan)
418
13
{
419
13
  double num;
420
421
39
  ZEND_PARSE_PARAMETERS_START(1, 1)
422
52
    Z_PARAM_DOUBLE(num)
423
13
  ZEND_PARSE_PARAMETERS_END();
424
13
  RETURN_DOUBLE(tan(num));
425
13
}
426
/* }}} */
427
428
/* {{{ Returns the arc sine of the number in radians */
429
PHP_FUNCTION(asin)
430
0
{
431
0
  double num;
432
433
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
434
0
    Z_PARAM_DOUBLE(num)
435
0
  ZEND_PARSE_PARAMETERS_END();
436
0
  RETURN_DOUBLE(asin(num));
437
0
}
438
/* }}} */
439
440
/* {{{ Return the arc cosine of the number in radians */
441
PHP_FUNCTION(acos)
442
0
{
443
0
  double num;
444
445
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
446
0
    Z_PARAM_DOUBLE(num)
447
0
  ZEND_PARSE_PARAMETERS_END();
448
0
  RETURN_DOUBLE(acos(num));
449
0
}
450
/* }}} */
451
452
/* {{{ Returns the arc tangent of the number in radians */
453
PHP_FUNCTION(atan)
454
0
{
455
0
  double num;
456
457
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
458
0
    Z_PARAM_DOUBLE(num)
459
0
  ZEND_PARSE_PARAMETERS_END();
460
0
  RETURN_DOUBLE(atan(num));
461
0
}
462
/* }}} */
463
464
/* {{{ Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x */
465
PHP_FUNCTION(atan2)
466
0
{
467
0
  double num1, num2;
468
469
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
470
0
    Z_PARAM_DOUBLE(num1)
471
0
    Z_PARAM_DOUBLE(num2)
472
0
  ZEND_PARSE_PARAMETERS_END();
473
0
  RETURN_DOUBLE(atan2(num1, num2));
474
0
}
475
/* }}} */
476
477
/* {{{ Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2 */
478
PHP_FUNCTION(sinh)
479
0
{
480
0
  double num;
481
482
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
483
0
    Z_PARAM_DOUBLE(num)
484
0
  ZEND_PARSE_PARAMETERS_END();
485
0
  RETURN_DOUBLE(sinh(num));
486
0
}
487
/* }}} */
488
489
/* {{{ Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2 */
490
PHP_FUNCTION(cosh)
491
0
{
492
0
  double num;
493
494
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
495
0
    Z_PARAM_DOUBLE(num)
496
0
  ZEND_PARSE_PARAMETERS_END();
497
0
  RETURN_DOUBLE(cosh(num));
498
0
}
499
/* }}} */
500
501
/* {{{ Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number) */
502
PHP_FUNCTION(tanh)
503
0
{
504
0
  double num;
505
506
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
507
0
    Z_PARAM_DOUBLE(num)
508
0
  ZEND_PARSE_PARAMETERS_END();
509
0
  RETURN_DOUBLE(tanh(num));
510
0
}
511
/* }}} */
512
513
/* {{{ Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number */
514
PHP_FUNCTION(asinh)
515
0
{
516
0
  double num;
517
518
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
519
0
    Z_PARAM_DOUBLE(num)
520
0
  ZEND_PARSE_PARAMETERS_END();
521
0
  RETURN_DOUBLE(asinh(num));
522
0
}
523
/* }}} */
524
525
/* {{{ Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number */
526
PHP_FUNCTION(acosh)
527
0
{
528
0
  double num;
529
530
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
531
0
    Z_PARAM_DOUBLE(num)
532
0
  ZEND_PARSE_PARAMETERS_END();
533
0
  RETURN_DOUBLE(acosh(num));
534
0
}
535
/* }}} */
536
537
/* {{{ Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number */
538
PHP_FUNCTION(atanh)
539
0
{
540
0
  double num;
541
542
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
543
0
    Z_PARAM_DOUBLE(num)
544
0
  ZEND_PARSE_PARAMETERS_END();
545
0
  RETURN_DOUBLE(atanh(num));
546
0
}
547
/* }}} */
548
549
/* {{{ Returns an approximation of pi */
550
PHP_FUNCTION(pi)
551
0
{
552
0
  ZEND_PARSE_PARAMETERS_NONE();
553
554
0
  RETURN_DOUBLE(M_PI);
555
0
}
556
/* }}} */
557
558
/* {{{ Returns whether argument is finite */
559
PHP_FUNCTION(is_finite)
560
5
{
561
5
  double dval;
562
563
15
  ZEND_PARSE_PARAMETERS_START(1, 1)
564
20
    Z_PARAM_DOUBLE(dval)
565
5
  ZEND_PARSE_PARAMETERS_END();
566
5
  RETURN_BOOL(zend_finite(dval));
567
5
}
568
/* }}} */
569
570
/* {{{ Returns whether argument is infinite */
571
PHP_FUNCTION(is_infinite)
572
0
{
573
0
  double dval;
574
575
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
576
0
    Z_PARAM_DOUBLE(dval)
577
0
  ZEND_PARSE_PARAMETERS_END();
578
0
  RETURN_BOOL(zend_isinf(dval));
579
0
}
580
/* }}} */
581
582
/* {{{ Returns whether argument is not a number */
583
PHP_FUNCTION(is_nan)
584
130
{
585
130
  double dval;
586
587
390
  ZEND_PARSE_PARAMETERS_START(1, 1)
588
520
    Z_PARAM_DOUBLE(dval)
589
130
  ZEND_PARSE_PARAMETERS_END();
590
130
  RETURN_BOOL(zend_isnan(dval));
591
130
}
592
/* }}} */
593
594
/* {{{ Returns base raised to the power of exponent. Returns integer result when possible */
595
PHP_FUNCTION(pow)
596
0
{
597
0
  zval *zbase, *zexp;
598
599
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
600
0
    Z_PARAM_ZVAL(zbase)
601
0
    Z_PARAM_ZVAL(zexp)
602
0
  ZEND_PARSE_PARAMETERS_END();
603
604
0
  pow_function(return_value, zbase, zexp);
605
0
}
606
/* }}} */
607
608
/* {{{ Returns e raised to the power of the number */
609
PHP_FUNCTION(exp)
610
0
{
611
0
  double num;
612
613
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
614
0
    Z_PARAM_DOUBLE(num)
615
0
  ZEND_PARSE_PARAMETERS_END();
616
617
0
  RETURN_DOUBLE(exp(num));
618
0
}
619
/* }}} */
620
621
/* {{{ Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
622
PHP_FUNCTION(expm1)
623
0
{
624
0
  double num;
625
626
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
627
0
    Z_PARAM_DOUBLE(num)
628
0
  ZEND_PARSE_PARAMETERS_END();
629
630
0
  RETURN_DOUBLE(expm1(num));
631
0
}
632
/* }}} */
633
634
/* {{{ Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */
635
PHP_FUNCTION(log1p)
636
0
{
637
0
  double num;
638
639
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
640
0
    Z_PARAM_DOUBLE(num)
641
0
  ZEND_PARSE_PARAMETERS_END();
642
643
0
  RETURN_DOUBLE(log1p(num));
644
0
}
645
/* }}} */
646
647
/* {{{ Returns the natural logarithm of the number, or the base log if base is specified */
648
PHP_FUNCTION(log)
649
10
{
650
10
  double num, base = 0;
651
652
30
  ZEND_PARSE_PARAMETERS_START(1, 2)
653
40
    Z_PARAM_DOUBLE(num)
654
10
    Z_PARAM_OPTIONAL
655
20
    Z_PARAM_DOUBLE(base)
656
10
  ZEND_PARSE_PARAMETERS_END();
657
658
10
  if (ZEND_NUM_ARGS() == 1) {
659
10
    RETURN_DOUBLE(log(num));
660
10
  }
661
662
0
  if (base == 2.0) {
663
0
    RETURN_DOUBLE(log2(num));
664
0
  }
665
666
0
  if (base == 10.0) {
667
0
    RETURN_DOUBLE(log10(num));
668
0
  }
669
670
0
  if (base == 1.0) {
671
0
    RETURN_DOUBLE(ZEND_NAN);
672
0
  }
673
674
0
  if (base <= 0.0) {
675
0
    zend_argument_value_error(2, "must be greater than 0");
676
0
    RETURN_THROWS();
677
0
  }
678
679
0
  RETURN_DOUBLE(log(num) / log(base));
680
0
}
681
/* }}} */
682
683
/* {{{ Returns the base-10 logarithm of the number */
684
PHP_FUNCTION(log10)
685
20
{
686
20
  double num;
687
688
60
  ZEND_PARSE_PARAMETERS_START(1, 1)
689
80
    Z_PARAM_DOUBLE(num)
690
20
  ZEND_PARSE_PARAMETERS_END();
691
692
20
  RETURN_DOUBLE(log10(num));
693
20
}
694
/* }}} */
695
696
/* {{{ Returns the square root of the number */
697
PHP_FUNCTION(sqrt)
698
15
{
699
15
  double num;
700
701
45
  ZEND_PARSE_PARAMETERS_START(1, 1)
702
60
    Z_PARAM_DOUBLE(num)
703
15
  ZEND_PARSE_PARAMETERS_END();
704
705
15
  RETURN_DOUBLE(sqrt(num));
706
15
}
707
/* }}} */
708
709
/* {{{ Returns sqrt(num1*num1 + num2*num2) */
710
PHP_FUNCTION(hypot)
711
0
{
712
0
  double num1, num2;
713
714
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
715
0
    Z_PARAM_DOUBLE(num1)
716
0
    Z_PARAM_DOUBLE(num2)
717
0
  ZEND_PARSE_PARAMETERS_END();
718
719
0
  RETURN_DOUBLE(hypot(num1, num2));
720
0
}
721
/* }}} */
722
723
/* {{{ Converts the number in degrees to the radian equivalent */
724
PHP_FUNCTION(deg2rad)
725
193
{
726
193
  double deg;
727
728
579
  ZEND_PARSE_PARAMETERS_START(1, 1)
729
772
    Z_PARAM_DOUBLE(deg)
730
193
  ZEND_PARSE_PARAMETERS_END();
731
193
  RETURN_DOUBLE((deg / 180.0) * M_PI);
732
193
}
733
/* }}} */
734
735
/* {{{ Converts the radian number to the equivalent number in degrees */
736
PHP_FUNCTION(rad2deg)
737
0
{
738
0
  double rad;
739
740
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
741
0
    Z_PARAM_DOUBLE(rad)
742
0
  ZEND_PARSE_PARAMETERS_END();
743
744
0
  RETURN_DOUBLE((rad / M_PI) * 180);
745
0
}
746
/* }}} */
747
748
/* {{{ _php_math_basetolong */
749
/*
750
 * Convert a string representation of a base(2-36) number to a long.
751
 */
752
PHPAPI zend_long _php_math_basetolong(zval *arg, int base)
753
0
{
754
0
  zend_long num = 0, digit, onum;
755
0
  zend_long i;
756
0
  char c, *s;
757
758
0
  if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
759
0
    return 0;
760
0
  }
761
762
0
  s = Z_STRVAL_P(arg);
763
764
0
  for (i = Z_STRLEN_P(arg); i > 0; i--) {
765
0
    c = *s++;
766
767
0
    digit = (c >= '0' && c <= '9') ? c - '0'
768
0
      : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
769
0
      : (c >= 'a' && c <= 'z') ? c - 'a' + 10
770
0
      : base;
771
772
0
    if (digit >= base) {
773
0
      continue;
774
0
    }
775
776
0
    onum = num;
777
0
    num = num * base + digit;
778
0
    if (num > onum)
779
0
      continue;
780
781
0
    {
782
783
0
      php_error_docref(NULL, E_WARNING, "Number %s is too big to fit in long", s);
784
0
      return ZEND_LONG_MAX;
785
0
    }
786
0
  }
787
788
0
  return num;
789
0
}
790
/* }}} */
791
792
/* {{{ _php_math_basetozval */
793
/*
794
 * Convert a string representation of a base(2-36) number to a zval.
795
 */
796
PHPAPI void _php_math_basetozval(zend_string *str, int base, zval *ret)
797
14
{
798
14
  zend_long num = 0;
799
14
  double fnum = 0;
800
14
  int mode = 0;
801
14
  char c, *s, *e;
802
14
  zend_long cutoff;
803
14
  int cutlim;
804
14
  int invalidchars = 0;
805
806
14
  s = ZSTR_VAL(str);
807
14
  e = s + ZSTR_LEN(str);
808
809
  /* Skip leading whitespace */
810
14
  while (s < e && isspace(*s)) s++;
811
  /* Skip trailing whitespace */
812
14
  while (s < e && isspace(*(e-1))) e--;
813
814
14
  if (e - s >= 2) {
815
14
    if (base == 16 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) s += 2;
816
14
    if (base == 8 && s[0] == '0' && (s[1] == 'o' || s[1] == 'O')) s += 2;
817
14
    if (base == 2 && s[0] == '0' && (s[1] == 'b' || s[1] == 'B')) s += 2;
818
14
  }
819
820
14
  cutoff = ZEND_LONG_MAX / base;
821
14
  cutlim = ZEND_LONG_MAX % base;
822
823
924
  while (s < e) {
824
910
    c = *s++;
825
826
    /* might not work for EBCDIC */
827
910
    if (c >= '0' && c <= '9')
828
910
      c -= '0';
829
0
    else if (c >= 'A' && c <= 'Z')
830
0
      c -= 'A' - 10;
831
0
    else if (c >= 'a' && c <= 'z')
832
0
      c -= 'a' - 10;
833
0
    else {
834
0
      invalidchars++;
835
0
      continue;
836
0
    }
837
838
910
    if (c >= base) {
839
0
      invalidchars++;
840
0
      continue;
841
0
    }
842
843
910
    switch (mode) {
844
898
    case 0: /* Integer */
845
898
      if (num < cutoff || (num == cutoff && c <= cutlim)) {
846
886
        num = num * base + c;
847
886
        break;
848
886
      } else {
849
12
        fnum = (double)num;
850
12
        mode = 1;
851
12
      }
852
12
      ZEND_FALLTHROUGH;
853
24
    case 1: /* Float */
854
24
      fnum = fnum * base + c;
855
910
    }
856
910
  }
857
858
14
  if (invalidchars > 0) {
859
0
    zend_error(E_DEPRECATED, "Invalid characters passed for attempted conversion, these have been ignored");
860
0
  }
861
862
14
  if (mode == 1) {
863
12
    ZVAL_DOUBLE(ret, fnum);
864
12
  } else {
865
2
    ZVAL_LONG(ret, num);
866
2
  }
867
14
}
868
/* }}} */
869
870
/* {{{ _php_math_longtobase */
871
/*
872
 * Convert a long to a string containing a base(2-36) representation of
873
 * the number.
874
 */
875
PHPAPI zend_string * _php_math_longtobase(zend_long arg, int base)
876
2
{
877
2
  static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
878
2
  char buf[(sizeof(zend_ulong) << 3) + 1];
879
2
  char *ptr, *end;
880
2
  zend_ulong value;
881
882
2
  if (base < 2 || base > 36) {
883
0
    return ZSTR_EMPTY_ALLOC();
884
0
  }
885
886
2
  value = arg;
887
888
2
  end = ptr = buf + sizeof(buf) - 1;
889
2
  *ptr = '\0';
890
891
2
  do {
892
2
    ZEND_ASSERT(ptr > buf);
893
2
    *--ptr = digits[value % base];
894
2
    value /= base;
895
2
  } while (value);
896
897
2
  return zend_string_init(ptr, end - ptr, 0);
898
2
}
899
/* }}} */
900
901
/* {{{ _php_math_longtobase_pwr2 */
902
/*
903
 * Convert a long to a string containing a base(2,4,6,16,32) representation of
904
 * the number.
905
 */
906
static zend_always_inline zend_string * _php_math_longtobase_pwr2(zend_long arg, int base_log2)
907
0
{
908
0
  static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
909
0
  zend_ulong value;
910
0
  size_t len;
911
0
  zend_string *ret;
912
0
  char *ptr;
913
914
0
  value = arg;
915
916
0
  if (value == 0) {
917
0
    len = 1;
918
0
  } else {
919
0
    len = ((sizeof(value) * 8 - zend_ulong_nlz(value)) + (base_log2 - 1)) / base_log2;
920
0
  }
921
922
0
  ret = zend_string_alloc(len, 0);
923
0
  ptr = ZSTR_VAL(ret) + len;
924
0
  *ptr = '\0';
925
926
0
  do {
927
0
    ZEND_ASSERT(ptr > ZSTR_VAL(ret));
928
0
    *--ptr = digits[value & ((1 << base_log2) - 1)];
929
0
    value >>= base_log2;
930
0
  } while (value);
931
932
0
  return ret;
933
0
}
934
/* }}} */
935
936
/* {{{ _php_math_zvaltobase */
937
/*
938
 * Convert a zval to a string containing a base(2-36) representation of
939
 * the number.
940
 */
941
PHPAPI zend_string * _php_math_zvaltobase(zval *arg, int base)
942
14
{
943
14
  static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
944
945
14
  if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
946
0
    return ZSTR_EMPTY_ALLOC();
947
0
  }
948
949
14
  if (Z_TYPE_P(arg) == IS_DOUBLE) {
950
12
    double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
951
12
    char *ptr, *end;
952
12
    char buf[(sizeof(double) << 3) + 1];
953
954
    /* Don't try to convert +/- infinity */
955
12
    if (fvalue == ZEND_INFINITY || fvalue == -ZEND_INFINITY) {
956
0
      zend_value_error("An infinite value cannot be converted to base %d", base);
957
0
      return NULL;
958
0
    }
959
960
12
    end = ptr = buf + sizeof(buf) - 1;
961
12
    *ptr = '\0';
962
963
240
    do {
964
240
      *--ptr = digits[(int) fmod(fvalue, base)];
965
240
      fvalue /= base;
966
240
    } while (ptr > buf && fabs(fvalue) >= 1);
967
968
12
    return zend_string_init(ptr, end - ptr, 0);
969
12
  }
970
971
2
  return _php_math_longtobase(Z_LVAL_P(arg), base);
972
14
}
973
/* }}} */
974
975
/* {{{ Returns the decimal equivalent of the binary number */
976
PHP_FUNCTION(bindec)
977
0
{
978
0
  zend_string *arg;
979
980
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
981
0
    Z_PARAM_STR(arg)
982
0
  ZEND_PARSE_PARAMETERS_END();
983
984
0
  _php_math_basetozval(arg, 2, return_value);
985
0
}
986
/* }}} */
987
988
/* {{{ Returns the decimal equivalent of the hexadecimal number */
989
PHP_FUNCTION(hexdec)
990
0
{
991
0
  zend_string *arg;
992
993
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
994
0
    Z_PARAM_STR(arg)
995
0
  ZEND_PARSE_PARAMETERS_END();
996
997
0
  _php_math_basetozval(arg, 16, return_value);
998
0
}
999
/* }}} */
1000
1001
/* {{{ Returns the decimal equivalent of an octal string */
1002
PHP_FUNCTION(octdec)
1003
0
{
1004
0
  zend_string *arg;
1005
1006
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
1007
0
    Z_PARAM_STR(arg)
1008
0
  ZEND_PARSE_PARAMETERS_END();
1009
1010
0
  _php_math_basetozval(arg, 8, return_value);
1011
0
}
1012
/* }}} */
1013
1014
/* {{{ Returns a string containing a binary representation of the number */
1015
PHP_FUNCTION(decbin)
1016
0
{
1017
0
  zend_long arg;
1018
1019
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
1020
0
    Z_PARAM_LONG(arg)
1021
0
  ZEND_PARSE_PARAMETERS_END();
1022
1023
0
  RETURN_STR(_php_math_longtobase_pwr2(arg, 1));
1024
0
}
1025
/* }}} */
1026
1027
/* {{{ Returns a string containing an octal representation of the given number */
1028
PHP_FUNCTION(decoct)
1029
0
{
1030
0
  zend_long arg;
1031
1032
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
1033
0
    Z_PARAM_LONG(arg)
1034
0
  ZEND_PARSE_PARAMETERS_END();
1035
1036
0
  RETURN_STR(_php_math_longtobase_pwr2(arg, 3));
1037
0
}
1038
/* }}} */
1039
1040
/* {{{ Returns a string containing a hexadecimal representation of the given number */
1041
PHP_FUNCTION(dechex)
1042
0
{
1043
0
  zend_long arg;
1044
1045
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
1046
0
    Z_PARAM_LONG(arg)
1047
0
  ZEND_PARSE_PARAMETERS_END();
1048
1049
0
  RETURN_STR(_php_math_longtobase_pwr2(arg, 4));
1050
0
}
1051
/* }}} */
1052
1053
ZEND_FRAMELESS_FUNCTION(dechex, 1)
1054
0
{
1055
0
  zend_long arg;
1056
1057
0
  Z_FLF_PARAM_LONG(1, arg);
1058
1059
0
  RETVAL_STR(_php_math_longtobase_pwr2(arg, 4));
1060
1061
0
flf_clean:;
1062
0
}
1063
1064
/* {{{ Converts a number in a string from any base <= 36 to any base <= 36 */
1065
PHP_FUNCTION(base_convert)
1066
14
{
1067
14
  zval temp;
1068
14
  zend_string *number;
1069
14
  zend_long frombase, tobase;
1070
14
  zend_string *result;
1071
1072
42
  ZEND_PARSE_PARAMETERS_START(3, 3)
1073
56
    Z_PARAM_STR(number)
1074
70
    Z_PARAM_LONG(frombase)
1075
70
    Z_PARAM_LONG(tobase)
1076
14
  ZEND_PARSE_PARAMETERS_END();
1077
1078
14
  if (frombase < 2 || frombase > 36) {
1079
0
    zend_argument_value_error(2, "must be between 2 and 36 (inclusive)");
1080
0
    RETURN_THROWS();
1081
0
  }
1082
14
  if (tobase < 2 || tobase > 36) {
1083
0
    zend_argument_value_error(3, "must be between 2 and 36 (inclusive)");
1084
0
    RETURN_THROWS();
1085
0
  }
1086
1087
14
  _php_math_basetozval(number, (int)frombase, &temp);
1088
14
  result = _php_math_zvaltobase(&temp, (int)tobase);
1089
14
  if (!result) {
1090
0
    RETURN_THROWS();
1091
0
  }
1092
1093
14
  RETVAL_STR(result);
1094
14
}
1095
/* }}} */
1096
1097
/* {{{ _php_math_number_format */
1098
PHPAPI zend_string *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
1099
0
{
1100
0
  return _php_math_number_format_ex(d, dec, &dec_point, 1, &thousand_sep, 1);
1101
0
}
1102
1103
PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, const char *dec_point,
1104
    size_t dec_point_len, const char *thousand_sep, size_t thousand_sep_len)
1105
0
{
1106
0
  zend_string *res;
1107
0
  zend_string *tmpbuf;
1108
0
  char *s, *t;  /* source, target */
1109
0
  char *dp;
1110
0
  size_t integral;
1111
0
  size_t reslen = 0;
1112
0
  int count = 0;
1113
0
  int is_negative = 0;
1114
1115
0
  if (d < 0) {
1116
0
    is_negative = 1;
1117
0
    d = -d;
1118
0
  }
1119
1120
0
  d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
1121
0
  dec = MAX(0, dec);
1122
0
  tmpbuf = strpprintf(0, "%.*F", dec, d);
1123
0
  if (tmpbuf == NULL) {
1124
0
    return NULL;
1125
0
  } else if (!isdigit((int)ZSTR_VAL(tmpbuf)[0])) {
1126
0
    return tmpbuf;
1127
0
  }
1128
1129
  /* Check if the number is no longer negative after rounding */
1130
0
  if (is_negative && d == 0) {
1131
0
    is_negative = 0;
1132
0
  }
1133
1134
  /* find decimal point, if expected */
1135
0
  if (dec) {
1136
0
    dp = strpbrk(ZSTR_VAL(tmpbuf), ".,");
1137
0
  } else {
1138
0
    dp = NULL;
1139
0
  }
1140
1141
  /* calculate the length of the return buffer */
1142
0
  if (dp) {
1143
0
    integral = (dp - ZSTR_VAL(tmpbuf));
1144
0
  } else {
1145
    /* no decimal point was found */
1146
0
    integral = ZSTR_LEN(tmpbuf);
1147
0
  }
1148
1149
  /* allow for thousand separators */
1150
0
  if (thousand_sep) {
1151
0
    integral = zend_safe_addmult((integral-1)/3, thousand_sep_len, integral, "number formatting");
1152
0
  }
1153
1154
0
  reslen = integral;
1155
1156
0
  if (dec) {
1157
0
    reslen += dec;
1158
1159
0
    if (dec_point) {
1160
0
      reslen = zend_safe_addmult(reslen, 1, dec_point_len, "number formatting");
1161
0
    }
1162
0
  }
1163
1164
  /* add a byte for minus sign */
1165
0
  if (is_negative) {
1166
0
    reslen++;
1167
0
  }
1168
0
  res = zend_string_alloc(reslen, 0);
1169
1170
0
  s = ZSTR_VAL(tmpbuf) + ZSTR_LEN(tmpbuf) - 1;
1171
0
  t = ZSTR_VAL(res) + reslen;
1172
0
  *t-- = '\0';
1173
1174
  /* copy the decimal places.
1175
   * Take care, as the sprintf implementation may return less places than
1176
   * we requested due to internal buffer limitations */
1177
0
  if (dec) {
1178
0
    size_t declen = (dp ? s - dp : 0);
1179
0
    size_t topad = (size_t)dec > declen ? dec - declen : 0;
1180
1181
    /* pad with '0's */
1182
0
    while (topad--) {
1183
0
      *t-- = '0';
1184
0
    }
1185
1186
0
    if (dp) {
1187
0
      s -= declen + 1; /* +1 to skip the point */
1188
0
      t -= declen;
1189
1190
      /* now copy the chars after the point */
1191
0
      memcpy(t + 1, dp + 1, declen);
1192
0
    }
1193
1194
    /* add decimal point */
1195
0
    if (dec_point) {
1196
0
      t -= dec_point_len;
1197
0
      memcpy(t + 1, dec_point, dec_point_len);
1198
0
    }
1199
0
  }
1200
1201
  /* copy the numbers before the decimal point, adding thousand
1202
   * separator every three digits */
1203
0
  while (s >= ZSTR_VAL(tmpbuf)) {
1204
0
    *t-- = *s--;
1205
0
    if (thousand_sep && (++count%3)==0 && s >= ZSTR_VAL(tmpbuf)) {
1206
0
      t -= thousand_sep_len;
1207
0
      memcpy(t + 1, thousand_sep, thousand_sep_len);
1208
0
    }
1209
0
  }
1210
1211
  /* and a minus sign, if needed */
1212
0
  if (is_negative) {
1213
0
    *t-- = '-';
1214
0
  }
1215
1216
0
  ZSTR_LEN(res) = reslen;
1217
0
  zend_string_release_ex(tmpbuf, 0);
1218
0
  return res;
1219
0
}
1220
1221
PHPAPI zend_string *_php_math_number_format_long(zend_long num, zend_long dec, const char *dec_point,
1222
    size_t dec_point_len, const char *thousand_sep, size_t thousand_sep_len)
1223
0
{
1224
0
  static const zend_ulong powers[] = {
1225
0
    1, 10, 100, 1000, 10000,
1226
0
    100000, 1000000, 10000000, 100000000, 1000000000,
1227
0
#if SIZEOF_ZEND_LONG == 8
1228
0
    10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000,
1229
0
    1000000000000000, 10000000000000000, 100000000000000000, 1000000000000000000, 10000000000000000000ul
1230
#elif SIZEOF_ZEND_LONG > 8
1231
# error "Unknown SIZEOF_ZEND_LONG"
1232
#endif
1233
0
  };
1234
1235
0
  int is_negative = 0;
1236
0
  zend_ulong tmpnum;
1237
0
  zend_ulong power;
1238
0
  zend_ulong power_half;
1239
0
  zend_ulong rest;
1240
1241
0
  zend_string *tmpbuf;
1242
0
  zend_string *res;
1243
0
  size_t reslen;
1244
0
  char *s, *t;  /* source, target */
1245
0
  int count = 0;
1246
0
  size_t topad;
1247
1248
  // unsigned absolute number and memorize negative sign
1249
0
  if (num < 0) {
1250
0
    is_negative = 1;
1251
0
    tmpnum = ((zend_ulong)-(num + 1)) + 1;
1252
0
  } else {
1253
0
    tmpnum = (zend_ulong)num;
1254
0
  }
1255
1256
  // rounding the number
1257
0
  if (dec < 0) {
1258
    // Check rounding to more negative places than possible
1259
0
    if (dec < -(sizeof(powers) / sizeof(powers[0]) - 1)) {
1260
0
      tmpnum = 0;
1261
0
    } else {
1262
0
      power = powers[-dec];
1263
0
      power_half = power / 2;
1264
0
      rest = tmpnum % power;
1265
0
      tmpnum = tmpnum / power;
1266
1267
0
      if (rest >= power_half) {
1268
0
        tmpnum = tmpnum * power + power;
1269
0
      } else {
1270
0
        tmpnum = tmpnum * power;
1271
0
      }
1272
0
    }
1273
1274
    // prevent resulting in negative zero
1275
0
    if (tmpnum == 0) {
1276
0
      is_negative = 0;
1277
0
    }
1278
0
  }
1279
1280
0
  tmpbuf = strpprintf(0, ZEND_ULONG_FMT, tmpnum);
1281
0
  reslen = ZSTR_LEN(tmpbuf);
1282
1283
  /* allow for thousand separators */
1284
0
  if (thousand_sep) {
1285
0
    reslen = zend_safe_addmult((reslen-1)/3, thousand_sep_len, reslen, "number formatting");
1286
0
  }
1287
1288
0
  reslen += is_negative;
1289
1290
0
  if (dec > 0) {
1291
0
    reslen += dec;
1292
1293
0
    if (dec_point) {
1294
0
      reslen = zend_safe_addmult(reslen, 1, dec_point_len, "number formatting");
1295
0
    }
1296
0
  }
1297
1298
0
  res = zend_string_alloc(reslen, 0);
1299
1300
0
  s = ZSTR_VAL(tmpbuf) + ZSTR_LEN(tmpbuf) - 1;
1301
0
  t = ZSTR_VAL(res) + reslen;
1302
0
  *t-- = '\0';
1303
1304
  /* copy the decimal places. */
1305
0
  if (dec > 0) {
1306
0
    topad = (size_t)dec;
1307
1308
    /* pad with '0's */
1309
0
    while (topad--) {
1310
0
      *t-- = '0';
1311
0
    }
1312
1313
    /* add decimal point */
1314
0
    if (dec_point) {
1315
0
      t -= dec_point_len;
1316
0
      memcpy(t + 1, dec_point, dec_point_len);
1317
0
    }
1318
0
  }
1319
1320
  /* copy the numbers before the decimal point, adding thousand
1321
   * separator every three digits */
1322
0
  while (s >= ZSTR_VAL(tmpbuf)) {
1323
0
    *t-- = *s--;
1324
0
    if (thousand_sep && (++count % 3) == 0 && s >= ZSTR_VAL(tmpbuf)) {
1325
0
      t -= thousand_sep_len;
1326
0
      memcpy(t + 1, thousand_sep, thousand_sep_len);
1327
0
    }
1328
0
  }
1329
1330
0
  if (is_negative) {
1331
0
    *t-- = '-';
1332
0
  }
1333
1334
0
  ZSTR_LEN(res) = reslen;
1335
0
  zend_string_release_ex(tmpbuf, 0);
1336
0
  return res;
1337
0
}
1338
1339
/* {{{ Formats a number with grouped thousands */
1340
PHP_FUNCTION(number_format)
1341
0
{
1342
0
  zval* num;
1343
0
  zend_long dec = 0;
1344
0
  int dec_int;
1345
0
  char *thousand_sep = NULL, *dec_point = NULL;
1346
0
  size_t thousand_sep_len = 0, dec_point_len = 0;
1347
1348
0
  ZEND_PARSE_PARAMETERS_START(1, 4)
1349
0
    Z_PARAM_NUMBER(num)
1350
0
    Z_PARAM_OPTIONAL
1351
0
    Z_PARAM_LONG(dec)
1352
0
    Z_PARAM_STRING_OR_NULL(dec_point, dec_point_len)
1353
0
    Z_PARAM_STRING_OR_NULL(thousand_sep, thousand_sep_len)
1354
0
  ZEND_PARSE_PARAMETERS_END();
1355
1356
0
  if (dec_point == NULL) {
1357
0
    dec_point = ".";
1358
0
    dec_point_len = 1;
1359
0
  }
1360
0
  if (thousand_sep == NULL) {
1361
0
    thousand_sep = ",";
1362
0
    thousand_sep_len = 1;
1363
0
  }
1364
1365
0
  switch (Z_TYPE_P(num)) {
1366
0
    case IS_LONG:
1367
0
      RETURN_STR(_php_math_number_format_long(Z_LVAL_P(num), dec, dec_point, dec_point_len, thousand_sep, thousand_sep_len));
1368
0
      break;
1369
1370
0
    case IS_DOUBLE:
1371
      // double values of >= 2^52 can not have fractional digits anymore
1372
      // Casting to long on 64bit will not loose precision on rounding
1373
0
      if (UNEXPECTED(
1374
0
        (Z_DVAL_P(num) >= 4503599627370496.0 || Z_DVAL_P(num) <= -4503599627370496.0)
1375
0
        && ZEND_DOUBLE_FITS_LONG(Z_DVAL_P(num))
1376
0
      )) {
1377
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));
1378
0
                break;
1379
0
      }
1380
1381
0
      if (dec >= 0) {
1382
0
        dec_int = ZEND_LONG_INT_OVFL(dec) ? INT_MAX : (int)dec;
1383
0
      } else {
1384
0
        dec_int = ZEND_LONG_INT_UDFL(dec) ? INT_MIN : (int)dec;
1385
0
      }
1386
0
      RETURN_STR(_php_math_number_format_ex(Z_DVAL_P(num), dec_int, dec_point, dec_point_len, thousand_sep, thousand_sep_len));
1387
0
      break;
1388
1389
0
    EMPTY_SWITCH_DEFAULT_CASE()
1390
0
  }
1391
0
}
1392
/* }}} */
1393
1394
/* {{{ Returns the remainder of dividing x by y as a float */
1395
PHP_FUNCTION(fmod)
1396
0
{
1397
0
  double num1, num2;
1398
1399
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
1400
0
    Z_PARAM_DOUBLE(num1)
1401
0
    Z_PARAM_DOUBLE(num2)
1402
0
  ZEND_PARSE_PARAMETERS_END();
1403
1404
0
  RETURN_DOUBLE(fmod(num1, num2));
1405
0
}
1406
/* }}} */
1407
1408
/* {{{ Perform floating-point division of dividend / divisor
1409
   with IEEE-754 semantics for division by zero. */
1410
#ifdef __clang__
1411
__attribute__((no_sanitize("float-divide-by-zero")))
1412
#endif
1413
PHP_FUNCTION(fdiv)
1414
24
{
1415
24
  double dividend, divisor;
1416
1417
72
  ZEND_PARSE_PARAMETERS_START(2, 2)
1418
96
    Z_PARAM_DOUBLE(dividend)
1419
120
    Z_PARAM_DOUBLE(divisor)
1420
24
  ZEND_PARSE_PARAMETERS_END();
1421
1422
24
  RETURN_DOUBLE(dividend / divisor);
1423
24
}
1424
/* }}} */
1425
1426
/* {{{ Perform floating-point exponentiation with IEEE-754 semantics. */
1427
PHP_FUNCTION(fpow)
1428
0
{
1429
0
  double base, exponent;
1430
1431
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
1432
0
    Z_PARAM_DOUBLE(base)
1433
0
    Z_PARAM_DOUBLE(exponent)
1434
0
  ZEND_PARSE_PARAMETERS_END();
1435
1436
0
  RETURN_DOUBLE(pow(base, exponent));
1437
0
}
1438
/* }}} */
1439
1440
/* {{{ Returns the integer quotient of the division of dividend by divisor */
1441
PHP_FUNCTION(intdiv)
1442
608
{
1443
608
  zend_long dividend, divisor;
1444
1445
1.82k
  ZEND_PARSE_PARAMETERS_START(2, 2)
1446
2.43k
    Z_PARAM_LONG(dividend)
1447
3.04k
    Z_PARAM_LONG(divisor)
1448
608
  ZEND_PARSE_PARAMETERS_END();
1449
1450
608
  if (divisor == 0) {
1451
0
    zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
1452
0
    RETURN_THROWS();
1453
608
  } else if (divisor == -1 && dividend == ZEND_LONG_MIN) {
1454
    /* Prevent overflow error/crash ... really should not happen:
1455
       We don't return a float here as that violates function contract */
1456
0
    zend_throw_exception_ex(zend_ce_arithmetic_error, 0, "Division of PHP_INT_MIN by -1 is not an integer");
1457
0
    RETURN_THROWS();
1458
0
  }
1459
1460
608
  RETURN_LONG(dividend / divisor);
1461
608
}
1462
/* }}} */