1
0
mirror of https://github.com/php/php-src.git synced 2026-03-25 16:52:18 +01:00
Files
archived-php-src/ext/bcmath/libbcmath/src/doaddsub.c
Saki Takamachi 25579a8616 ext/bcmath: Renamed macros and variables (#14507)
Made the macro BC_UINT_T a typedef and renamed it BC_VECTOR.

Additionally, several macros have been renamed to be consistent with BC_VECTOR.
2024-06-09 09:11:11 +09:00

297 lines
8.6 KiB
C

/* doaddsub.c: bcmath library file. */
/*
Copyright (C) 1991, 1992, 1993, 1994, 1997 Free Software Foundation, Inc.
Copyright (C) 2000 Philip A. Nelson
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details. (LICENSE)
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place, Suite 330
Boston, MA 02111-1307 USA.
You may contact the author by:
e-mail: philnelson@acm.org
us-mail: Philip A. Nelson
Computer Science Department, 9062
Western Washington University
Bellingham, WA 98226-9062
*************************************************************************/
#include "bcmath.h"
#include "private.h"
#include <stddef.h>
/* Perform addition: N1 is added to N2 and the value is
returned. The signs of N1 and N2 are ignored.
SCALE_MIN is to set the minimum scale of the result. */
bc_num _bc_do_add(bc_num n1, bc_num n2)
{
bc_num sum;
size_t sum_len = MAX(n1->n_len, n2->n_len) + 1;
size_t sum_scale = MAX(n1->n_scale, n2->n_scale);
size_t min_len = MIN (n1->n_len, n2->n_len);
size_t min_scale = MIN(n1->n_scale, n2->n_scale);
size_t min_bytes = min_len + min_scale;
char *n1ptr, *n2ptr, *sumptr;
bool carry = 0;
size_t count;
/* Prepare sum. */
sum = bc_new_num_nonzeroed(sum_len, sum_scale);
/* Start with the fraction part. Initialize the pointers. */
n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale - 1);
n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale - 1);
sumptr = (char *) (sum->n_value + sum_scale + sum_len - 1);
/* Add the fraction part. First copy the longer fraction.*/
if (n1->n_scale != min_scale) {
/* n1 has the longer scale */
for (count = n1->n_scale - min_scale; count > 0; count--) {
*sumptr-- = *n1ptr--;
}
} else {
/* n2 has the longer scale */
for (count = n2->n_scale - min_scale; count > 0; count--) {
*sumptr-- = *n2ptr--;
}
}
/* Now add the remaining fraction part and equal size integer parts. */
count = 0;
/* Uses SIMD to perform calculations at high speed. */
if (min_bytes >= sizeof(BC_VECTOR)) {
sumptr++;
n1ptr++;
n2ptr++;
while (count + sizeof(BC_VECTOR) <= min_bytes) {
sumptr -= sizeof(BC_VECTOR);
n1ptr -= sizeof(BC_VECTOR);
n2ptr -= sizeof(BC_VECTOR);
BC_VECTOR n1bytes;
BC_VECTOR n2bytes;
memcpy(&n1bytes, n1ptr, sizeof(n1bytes));
memcpy(&n2bytes, n2ptr, sizeof(n2bytes));
#if BC_LITTLE_ENDIAN
/* Little endian requires changing the order of bytes. */
n1bytes = BC_BSWAP(n1bytes);
n2bytes = BC_BSWAP(n2bytes);
#endif
/*
* In order to add 1 to the "next digit" when a carry occurs, adjust it so that it
* overflows when add 10.
* e.g.
* 00001001(9) + 00000001(1) = 00001010(10) to
* 11111111 + 00000001 = 00000000(0) and carry 1
*/
n1bytes += SWAR_REPEAT(0xF6) + n2bytes + carry;
/* If the most significant bit is 0, a carry has occurred. */
carry = !(n1bytes & ((BC_VECTOR) 1 << (8 * sizeof(BC_VECTOR) - 1)));
/*
* The calculation result is a mixture of bytes that have been carried and bytes that have not.
* The most significant bit of each byte is 0 if it is carried forward, and 1 if it is not.
* Using this, subtract the 0xF6 added for adjustment from the byte that has not been carried
* over to return it to the correct value as a decimal number.
*/
BC_VECTOR sum_mask = ((n1bytes & SWAR_REPEAT(0x80)) >> 7) * 0xF6;
n1bytes -= sum_mask;
#if BC_LITTLE_ENDIAN
/* Little endian requires changing the order of bytes back. */
n1bytes = BC_BSWAP(n1bytes);
#endif
memcpy(sumptr, &n1bytes, sizeof(n1bytes));
count += sizeof(BC_VECTOR);
}
sumptr--;
n1ptr--;
n2ptr--;
}
for (; count < min_bytes; count++) {
*sumptr = *n1ptr-- + *n2ptr-- + carry;
if (*sumptr >= BASE) {
*sumptr -= BASE;
carry = 1;
} else {
carry = 0;
}
sumptr--;
}
/* Now add carry the longer integer part. */
if (n1->n_len != n2->n_len) {
if (n2->n_len > n1->n_len) {
n1ptr = n2ptr;
}
for (count = sum_len - min_len; count > 1; count--) {
*sumptr = *n1ptr-- + carry;
if (*sumptr >= BASE) {
*sumptr -= BASE;
carry = 1;
} else {
carry = 0;
}
sumptr--;
}
}
/* Set final carry. */
*sumptr = carry;
/* Adjust sum and return. */
_bc_rm_leading_zeros(sum);
return sum;
}
/* Perform subtraction: N2 is subtracted from N1 and the value is
returned. The signs of N1 and N2 are ignored. Also, N1 is
assumed to be larger than N2. SCALE_MIN is the minimum scale
of the result. */
bc_num _bc_do_sub(bc_num n1, bc_num n2)
{
bc_num diff;
/* The caller is guaranteed that n1 is always large. */
size_t diff_len = EXPECTED(n1->n_len >= n2->n_len) ? n1->n_len : n2->n_len;
size_t diff_scale = MAX(n1->n_scale, n2->n_scale);
/* Same condition as EXPECTED before, but using EXPECTED again will make it slower. */
size_t min_len = n1->n_len >= n2->n_len ? n2->n_len : n1->n_len;
size_t min_scale = MIN(n1->n_scale, n2->n_scale);
size_t min_bytes = min_len + min_scale;
size_t borrow = 0;
size_t count;
int val;
char *n1ptr, *n2ptr, *diffptr;
/* Allocate temporary storage. */
diff = bc_new_num_nonzeroed(diff_len, diff_scale);
/* Initialize the subtract. */
n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale - 1);
n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale - 1);
diffptr = (char *) (diff->n_value + diff_len + diff_scale - 1);
/* Take care of the longer scaled number. */
if (n1->n_scale != min_scale) {
/* n1 has the longer scale */
for (count = n1->n_scale - min_scale; count > 0; count--) {
*diffptr-- = *n1ptr--;
}
} else {
/* n2 has the longer scale */
for (count = n2->n_scale - min_scale; count > 0; count--) {
val = -*n2ptr-- - borrow;
if (val < 0) {
val += BASE;
borrow = 1;
} else {
borrow = 0;
}
*diffptr-- = val;
}
}
/* Now do the equal length scale and integer parts. */
count = 0;
/* Uses SIMD to perform calculations at high speed. */
if (min_bytes >= sizeof(BC_VECTOR)) {
diffptr++;
n1ptr++;
n2ptr++;
while (count + sizeof(BC_VECTOR) <= min_bytes) {
diffptr -= sizeof(BC_VECTOR);
n1ptr -= sizeof(BC_VECTOR);
n2ptr -= sizeof(BC_VECTOR);
BC_VECTOR n1bytes;
BC_VECTOR n2bytes;
memcpy(&n1bytes, n1ptr, sizeof(n1bytes));
memcpy(&n2bytes, n2ptr, sizeof(n2bytes));
#if BC_LITTLE_ENDIAN
/* Little endian requires changing the order of bytes. */
n1bytes = BC_BSWAP(n1bytes);
n2bytes = BC_BSWAP(n2bytes);
#endif
n1bytes -= n2bytes + borrow;
/* If the most significant bit is 1, a carry down has occurred. */
bool tmp_borrow = n1bytes & ((BC_VECTOR) 1 << (8 * sizeof(BC_VECTOR) - 1));
/*
* Check the most significant bit of each of the bytes, and if it is 1, a carry down has
* occurred. When carrying down occurs, due to the difference between decimal and hexadecimal
* numbers, an extra 6 is added to the lower 4 bits.
* Therefore, for a byte that has been carried down, set all the upper 4 bits to 0 and subtract
* 6 from the lower 4 bits to adjust it to the correct value as a decimal number.
*/
BC_VECTOR borrow_mask = ((n1bytes & SWAR_REPEAT(0x80)) >> 7) * 0x06;
n1bytes = (n1bytes & SWAR_REPEAT(0x0F)) - borrow_mask;
#if BC_LITTLE_ENDIAN
/* Little endian requires changing the order of bytes back. */
n1bytes = BC_BSWAP(n1bytes);
#endif
memcpy(diffptr, &n1bytes, sizeof(n1bytes));
borrow = tmp_borrow;
count += sizeof(BC_VECTOR);
}
diffptr--;
n1ptr--;
n2ptr--;
}
/* Calculate the remaining bytes that are less than the size of BC_VECTOR using a normal loop. */
for (; count < min_bytes; count++) {
val = *n1ptr-- - *n2ptr-- - borrow;
if (val < 0) {
val += BASE;
borrow = 1;
} else {
borrow = 0;
}
*diffptr-- = val;
}
/* If n1 has more digits than n2, we now do that subtract. */
if (diff_len != min_len) {
for (count = diff_len - min_len; count > 0; count--) {
val = *n1ptr-- - borrow;
if (val < 0) {
val += BASE;
borrow = 1;
} else {
borrow = 0;
}
*diffptr-- = val;
}
}
/* Clean up and return. */
_bc_rm_leading_zeros(diff);
return diff;
}