php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
round.c
Go to the documentation of this file.
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: Saki Takamachi <saki@php.net> |
14 +----------------------------------------------------------------------+
15*/
16
17#include "bcmath.h"
18#include "private.h"
19#include <stddef.h>
20
22{
23 /* clear result */
25
26 /*
27 * The following cases result in an early return:
28 *
29 * - When rounding to an integer part which is larger than the number
30 * e.g. Rounding 21.123 to 3 digits before the decimal point.
31 * - When rounding to a greater decimal precision then the number has, the number is unchanged
32 * e.g. Rounding 21.123 to 4 digits after the decimal point.
33 * - If the fractional part ends with zeros, the zeros are omitted and the number of digits in num is reduced.
34 * Meaning we might end up in the previous case.
35 */
36
37 /* e.g. value is 0.1 and precision is -3, ret is 0 or 1000 */
38 if (precision < 0 && num->n_len < (size_t) (-(precision + Z_L(1))) + 1) {
39 switch (mode) {
45 *result = bc_copy_num(BCG(_zero_));
46 return;
47
49 if (num->n_sign == MINUS) {
50 *result = bc_copy_num(BCG(_zero_));
51 return;
52 }
53 break;
54
55 case PHP_ROUND_FLOOR:
56 if (num->n_sign == PLUS) {
57 *result = bc_copy_num(BCG(_zero_));
58 return;
59 }
60 break;
61
63 break;
64
66 }
67
68 if (bc_is_zero(num)) {
69 *result = bc_copy_num(BCG(_zero_));
70 return;
71 }
72
73 /* If precision is -3, it becomes 1000. */
74 if (UNEXPECTED(precision == ZEND_LONG_MIN)) {
75 *result = bc_new_num((size_t) ZEND_LONG_MAX + 2, 0);
76 } else {
77 *result = bc_new_num(-precision + 1, 0);
78 }
79 (*result)->n_value[0] = 1;
80 (*result)->n_sign = num->n_sign;
81 return;
82 }
83
84 /* Just like bcadd('1', '1', 4) becomes '2.0000', it pads with zeros at the end if necessary. */
85 if (precision >= 0 && num->n_scale <= precision) {
86 if (num->n_scale == precision) {
87 *result = bc_copy_num(num);
88 } else if(num->n_scale < precision) {
89 *result = bc_new_num(num->n_len, precision);
90 (*result)->n_sign = num->n_sign;
91 memcpy((*result)->n_value, num->n_value, num->n_len + num->n_scale);
92 }
93 return;
94 }
95
96 /*
97 * If the calculation result is a negative value, there is an early return,
98 * so no underflow will occur.
99 */
100 size_t rounded_len = num->n_len + precision;
101
102 /*
103 * Initialize result
104 * For example, if rounded_len is 0, it means trying to round 50 to 100 or 0.
105 * If the result of rounding is carried over, it will be added later, so first set it to 0 here.
106 */
107 if (rounded_len == 0) {
108 *result = bc_new_num(1, 0);
109 } else {
110 *result = bc_new_num(num->n_len, precision > 0 ? precision : 0);
111 memcpy((*result)->n_value, num->n_value, rounded_len);
112 }
113 (*result)->n_sign = num->n_sign;
114
115 const char *nptr = num->n_value + rounded_len;
116
117 /* Check cases that can be determined without looping. */
118 switch (mode) {
120 if (*nptr >= 5) {
121 goto up;
122 } else if (*nptr < 5) {
123 goto check_zero;
124 }
125 break;
126
130 if (*nptr > 5) {
131 goto up;
132 } else if (*nptr < 5) {
133 goto check_zero;
134 }
135 /* if *nptr == 5, we need to look-up further digits before making a decision. */
136 break;
137
139 if (num->n_sign != PLUS) {
140 goto check_zero;
141 } else if (*nptr > 0) {
142 goto up;
143 }
144 /* if *nptr == 0, a loop is required for judgment. */
145 break;
146
147 case PHP_ROUND_FLOOR:
148 if (num->n_sign != MINUS) {
149 goto check_zero;
150 } else if (*nptr > 0) {
151 goto up;
152 }
153 /* if *nptr == 0, a loop is required for judgment. */
154 break;
155
157 goto check_zero;
158
160 if (*nptr > 0) {
161 goto up;
162 }
163 /* if *nptr == 0, a loop is required for judgment. */
164 break;
165
167 }
168
169 /* Loop through the remaining digits. */
170 size_t count = num->n_len + num->n_scale - rounded_len - 1;
171 nptr++;
172 while ((count > 0) && (*nptr == 0)) {
173 count--;
174 nptr++;
175 }
176
177 if (count > 0) {
178 goto up;
179 }
180
181 switch (mode) {
184 case PHP_ROUND_FLOOR:
186 goto check_zero;
187
189 if (rounded_len == 0 || num->n_value[rounded_len - 1] % 2 == 0) {
190 goto check_zero;
191 }
192 break;
193
195 if (rounded_len != 0 && num->n_value[rounded_len - 1] % 2 == 1) {
196 goto check_zero;
197 }
198 break;
199
201 }
202
203up:
204 {
205 bc_num tmp;
206
207 if (rounded_len == 0) {
208 tmp = bc_new_num(num->n_len + 1, 0);
209 tmp->n_value[0] = 1;
210 tmp->n_sign = num->n_sign;
211 } else {
212 bc_num scaled_one = bc_new_num((*result)->n_len, (*result)->n_scale);
213 scaled_one->n_value[rounded_len - 1] = 1;
214
215 tmp = _bc_do_add(*result, scaled_one);
216 tmp->n_sign = (*result)->n_sign;
217 bc_free_num(&scaled_one);
218 }
219
221 *result = tmp;
222 }
223
224check_zero:
225 if (bc_is_zero(*result)) {
226 (*result)->n_sign = PLUS;
227 }
228}
count(Countable|array $value, int $mode=COUNT_NORMAL)
bool bc_is_zero(bc_num num)
Definition zero.c:58
struct bc_struct * bc_num
Definition bcmath.h:39
#define bc_free_num(num)
Definition bcmath.h:187
@ PLUS
Definition bcmath.h:37
@ MINUS
Definition bcmath.h:37
#define bc_new_num(length, scale)
Definition bcmath.h:185
bc_num _bc_do_add(bc_num n1, bc_num n2)
Definition doaddsub.c:41
memcpy(ptr1, ptr2, size)
char * mode
bc_num _zero_
Definition php_bcmath.h:33
#define BCG(v)
Definition php_bcmath.h:46
#define PHP_ROUND_TOWARD_ZERO
#define PHP_ROUND_HALF_UP
#define PHP_ROUND_HALF_DOWN
#define PHP_ROUND_FLOOR
#define PHP_ROUND_HALF_EVEN
#define PHP_ROUND_AWAY_FROM_ZERO
#define PHP_ROUND_HALF_ODD
#define PHP_ROUND_CEILING
void bc_round(bc_num num, zend_long precision, zend_long mode, bc_num *result)
Definition round.c:21
sign n_sign
Definition bcmath.h:46
size_t n_len
Definition bcmath.h:42
size_t n_scale
Definition bcmath.h:43
char * n_value
Definition bcmath.h:44
int32_t zend_long
Definition zend_long.h:42
#define ZEND_LONG_MIN
Definition zend_long.h:46
#define Z_L(i)
Definition zend_long.h:48
#define ZEND_LONG_MAX
Definition zend_long.h:45
#define EMPTY_SWITCH_DEFAULT_CASE()
#define UNEXPECTED(condition)
bool result