mirror of
https://github.com/php/php-src.git
synced 2026-03-25 16:52:18 +01:00
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.
297 lines
8.6 KiB
C
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;
|
|
}
|