mp.c

来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 1,082 行 · 第 1/2 页

C
1,082
字号
/****************************************************************************
*
*                            Open Watcom Project
*
*    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
*  ========================================================================
*
*    This file contains Original Code and/or Modifications of Original
*    Code as defined in and that are subject to the Sybase Open Watcom
*    Public License version 1.0 (the 'License'). You may not use this file
*    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
*    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
*    provided with the Original Code and Modifications, and is also
*    available at www.sybase.com/developer/opensource.
*
*    The Original Code and all software distributed under the License are
*    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
*    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
*    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
*    NON-INFRINGEMENT. Please see the License for the specific language
*    governing rights and limitations under the License.
*
*  ========================================================================
*
* Description:  WHEN YOU FIGURE OUT WHAT THIS FILE DOES, PLEASE
*               DESCRIBE IT HERE!
*
****************************************************************************/


/* This module contains functions for doing multiple precision, positive
 * integer arithmetic.  Allocations for these numbers increase automatically
 * as required when doing the arithmetic.  The size of a number can grow until
 * there is no more heap space.
 */
#ifndef TEST
#include header
#else
#include <malloc.h>
#define _MemoryAllocate( size )         malloc( size )
#define _MemoryFree( ptr )              free( ptr )
#endif
#include "mp.h"
#include <limits.h>
#include <string.h>
#include "fp.h"

#define two_to_the_31           0x80000000
#define two_to_the_32           (uint64)0x100000000

#define MP_GROW( num, size ) \
        if( mp_grow( num, size ) == MP_OUT_OF_MEMORY ) { \
            return MP_OUT_OF_MEMORY; \
        }
#define MP_COPY( dst, src ) \
        if( mp_copy( dst, src ) == MP_OUT_OF_MEMORY ) { \
            return MP_OUT_OF_MEMORY; \
        }
#define MP_INIT( num, val ) \
        if( mp_init( num, val ) == MP_OUT_OF_MEMORY ) { \
            return MP_OUT_OF_MEMORY; \
        }

/* initialize an mpnum */
int mp_init( mpnum *mp, uint64 value )
{
    mp->num = (uint32*)_MemoryAllocate( sizeof(uint32) * 2 );
    if( mp->num == NULL ) return MP_OUT_OF_MEMORY;
    memset( mp->num, 0, sizeof(uint32) * 2 );
    mp->allocated = 2;
    if( value >= two_to_the_32 ) {
        mp->num[0] = (uint32)value;
        mp->num[1] = value>>32;
        mp->len = 2;
    } else {
        mp->num[0] = value;
        mp->len = 1;
    }
    return MP_NO_ERR;
}

/* extend the number of uint32s allocated to src by size */
int mp_grow( mpnum *src, uint32 size )
{
    uint32 *temp;
    size = max( 3, size );
    temp = (uint32*)_MemoryAllocate( sizeof(uint32) * (src->allocated + size) );
    if( temp == NULL ) return MP_OUT_OF_MEMORY;
    memcpy( temp, src->num, src->allocated * sizeof(uint32) );
    memset( &temp[src->allocated], 0, size * sizeof(uint32) );
    _MemoryFree( src->num );
    src->num = temp;
    src->allocated += size;
    return MP_NO_ERR;
}

/* free the array of uint32s */
int mp_free( mpnum *num )
{
    if( num->allocated > 0 ) {
        _MemoryFree( num->num );
    }
    num->num = NULL;
    num->allocated = 0;
    num->len = 0;
    return MP_NO_ERR;
}

/* set num to zero */
int mp_zero( mpnum *num )
{
    memset( num->num, 0, num->allocated * sizeof(uint32) );
    num->len = 0;
    return MP_NO_ERR;
}

/* copy the src number to the destination number; dst must have already been
 * initialized */
int mp_copy( mpnum *dst, mpnum *src )
{
    if( dst->allocated < src->len ) {
        MP_GROW( dst, src->len - dst->allocated );
    }
    memcpy( dst->num, src->num, src->len * sizeof(uint32) );
    dst->len = src->len;
    return MP_NO_ERR;
}

/* compare num1 and num2
 * returns:  -1 if num1 > num2
 *            0 if num1 = num2
 *            1 if num1 < num2
 */
int mp_cmp( mpnum *num1, mpnum *num2 )
{
    uint32 i;
    uint32 minlen = min( num1->len, num2->len );

    if( num1->len > num2->len ) {
        for( i = num1->len - 1; i >= minlen; i-- ) {
            if( num1->num[i] > 0 ) return -1;
            if( i == 0 ) break;
        }
    } else if( num1->len < num2->len ) {
        for( i = num2->len - 1; i >= minlen; i-- ) {
            if( num2->num[i] > 0 ) return 1;
            if( i == 0 ) break;
        }
    }
    if( minlen == 0 ) return 0;
    for( i = minlen - 1; ; i-- ) {
        if( num1->num[i] > num2->num[i] ) {
            return -1;
        } else if( num1->num[i] < num2->num[i] ) {
            return 1;
        }
        if( i == 0 ) break;
    }
    return 0;
}

int mp_gt( mpnum *num1, mpnum *num2 )
{
    if( mp_cmp( num1, num2 ) < 0 ) return TRUE;
    return FALSE;
}

int mp_gte( mpnum *num1, mpnum *num2 )
{
    if( mp_cmp( num1, num2 ) <= 0 ) return TRUE;
    return FALSE;
}

int mp_lt( mpnum *num1, mpnum *num2 )
{
    if( mp_cmp( num1, num2 ) > 0 ) return TRUE;
    return FALSE;
}

int mp_lte( mpnum *num1, mpnum *num2 )
{
    if( mp_cmp( num1, num2 ) >= 0 ) return TRUE;
    return FALSE;
}

int mp_eq( mpnum *num1, mpnum *num2 )
{
    if( mp_cmp( num1, num2 ) == 0 ) return TRUE;
    return FALSE;
}

int mp_ne( mpnum *num1, mpnum *num2 )
{
    if( mp_cmp( num1, num2 ) == 0 ) return FALSE;
    return TRUE;
}

/* shift src 'bits' number of bits to the left */
int mp_shiftleft( mpnum *dst, mpnum *src, uint32 bits )
{
    uint32 newsize = src->len + ((bits-1) / 32) + 1;
    uint32 cursrc = src->len - 1;
    uint32 curdst = newsize - 1;
    uint32 temp1, temp2;
    uint8  shift = ((bits-1) % 32) + 1;
    uint32 i;

    if( bits == 0 || src->len == 0 ) {
        if( dst != src ) MP_COPY( dst, src );
        return MP_NO_ERR;
    }

    if( dst->allocated < newsize ) {
        MP_GROW( dst, newsize - dst->allocated );
    }
    for( i = dst->allocated-1; i >= newsize - 1; i-- ) {
        dst->num[i] = 0;
    }
    for(;;cursrc--,curdst--) {
        temp1 = src->num[cursrc] >> (32-shift);
        if( shift == 32 ) {
            temp2 = 0;
        } else {
            temp2 = src->num[cursrc] << shift;
        }
        dst->num[curdst] |= temp1;
        dst->num[curdst-1] = temp2;
        if( cursrc == 0 ) break;
    }
    for( curdst--;; curdst-- ) {
        if( curdst == 0 ) break;
        dst->num[curdst-1] = 0;
    }
    dst->len = newsize;
    if( dst->num[newsize-1] == 0 ) {
        dst->len -= 1;
    }
    return MP_NO_ERR;
}

/* shift src 'bits' number of bits to the right */
int mp_shiftright( mpnum *dst, mpnum *src, uint32 bits )
{
    uint32 newsize;
    int temp = src->len - bits / 32;
    uint32 cursrc = bits / 32;
    uint32 curdst = 0;
    uint32 temp1, temp2;
    uint8  shift = bits % 32;

    if( temp <= 0 ) {
        mp_zero( dst );
        return MP_NO_ERR;
    }
    newsize = (uint32)temp;

    if( bits == 0 || src->len == 0 ) {
        if( dst != src ) MP_COPY( dst, src );
        return MP_NO_ERR;
    }

    if( dst->allocated < newsize ) {
        MP_GROW( dst, newsize - dst->allocated );
    }
    for(;;cursrc++,curdst++) {
        if( shift == 0 ) {
            temp1 = 0;
        } else {
            temp1 = src->num[cursrc] << (32-shift);
        }
        temp2 = src->num[cursrc] >> shift;
        if( curdst > 0 ) dst->num[curdst-1] |= temp1;
        dst->num[curdst] = temp2;
        if( cursrc == src->len - 1 ) break;
    }
    for( curdst++;; curdst++ ) {
        if( curdst >= newsize ) break;
        dst->num[curdst] = 0;
    }
    dst->len = newsize;
    if( dst->num[newsize-1] == 0 ) {
        dst->len -= 1;
    }

    return MP_NO_ERR;
}

/* add a scalar value to src */
int mp_addsc( mpnum *dst, mpnum *src, uint32 scalar )
{
    uint32 maxlen = src->len;
    uint64 sum = 0;
    uint64 carry = scalar;
    uint32 i;

    if( dst->allocated < maxlen + 1 ) {
        MP_GROW( dst, maxlen + 1 - dst->allocated );
    }
    for( i = 0; ; i++ ) {
        if( i >= src->len ) {
            if( carry > 0 ) {
                dst->num[i] = carry;
            }
            break;
        } else {
            sum = (uint64)src->num[i] + carry;
        }
        if( sum >= two_to_the_32 ) {
            dst->num[i] = sum - two_to_the_32;
            carry = 1;
        } else {
            dst->num[i] = sum;
            carry = 0;
            break;
        }
    }
    if( dst->num[maxlen] == 0 ) {
        dst->len = maxlen;
    } else {
        dst->len = maxlen + 1;
    }
    return MP_NO_ERR;
}

/* add two mpnums */
int mp_add( mpnum *dst, mpnum *src1, mpnum *src2 )
{
    uint32 maxlen = max( src1->len, src2->len );
    uint64 sum = 0;
    uint64 carry = 0;
    uint32 i;

    if( dst->allocated < maxlen + 1 ) {
        MP_GROW( dst, maxlen + 1 - dst->allocated );
    }
    for( i = 0; ; i++ ) {
        if( i >= src1->len && i >= src2->len ) {
            if( carry > 0 ) {
                dst->num[i] = carry;
                dst->len = maxlen + 1;
            } else {
                dst->len = maxlen;
            }
            break;
        } else if( i >= src1->len ) {
            sum = (uint64)src2->num[i] + carry;
        } else if( i >= src2->len ) {
            sum = (uint64)src1->num[i] + carry;
        } else {
            sum = (uint64)src1->num[i] + (uint64)src2->num[i] + carry;
        }
        if( sum >= two_to_the_32 ) {
            dst->num[i] = sum - two_to_the_32;
            carry = 1;
        } else {
            dst->num[i] = sum;
            carry = 0;
        }
    }
    return MP_NO_ERR;
}

/* subtract two npnums; returns MP_NEGATIVE_RESULT if src1 < src2 */
int mp_sub( mpnum *dst, mpnum *src1, mpnum *src2 )
{
    uint32 maxlen = max( src1->len, src2->len );
    uint32 carry = 0;
    uint32 i;
    uint32 temp;

    if( mp_lt( src1, src2 ) ) {
        /* src1 < src2 => error */
        return MP_NEGATIVE_RESULT;
    }
    /* ASSERT num1 >= num2 => don't need to look for errors */
    if( dst->allocated < maxlen ) {
        MP_GROW( dst, maxlen - dst->allocated );
    }
    for( i = 0; ; i++ ) {
        if( i >= src1->len && i >= src2->len ) {
            break;
        } else if( i >= src2->len ) {
            temp = 0;
        } else {
            temp = src2->num[i];
        }
        if( src1->num[i] >= temp + carry ) {
            dst->num[i] = src1->num[i] - temp - carry;
            carry = 0;
        } else {
            dst->num[i] = (uint64)src1->num[i] + two_to_the_32 - temp - carry;
            carry = 1;
        }
    }
    for( i = maxlen - 1; ; i-- ) {
        if( dst->num[i] > 0 ) {
            dst->len = i + 1;
            break;
        }
        if( i == 0 ) {
            dst->len = 0;
            break;
        }
    }
    return MP_NO_ERR;
}

/* multiply an mpnum by a scalar value */
int mp_mulsc( mpnum *dst, mpnum *src, uint32 scalar )
{
    uint64 product = 0;
    uint64 carry = 0;
    uint32 i;

    if( dst->allocated < src->len + 1 ) {
        MP_GROW( dst, src->len + 1 - dst->allocated );
    }
    for( i = 0; ; i++ ) {
        if( i >= src->len ) {
            if( carry > 0 ) {
                dst->num[i] = carry;
                dst->len = src->len + 1;
            } else {
                dst->len = src->len;
            }
            break;
        } else {
            product = (uint64)src->num[i] * (uint64)scalar + carry;
        }
        if( product >= two_to_the_32 ) {
            dst->num[i] = (uint32)product;
            carry = product>>32;
        } else {
            dst->num[i] = product;
            carry = 0;
        }
    }
    return MP_NO_ERR;
}

/* Multiply two mpnums together using the intuitive pen-and-paper method for
 * multilpication.
 */
int mp_mul( mpnum *dst, mpnum *src1, mpnum *src2 )
{
    uint32 cur;
    uint32 sc;
    mpnum temp;
    mpnum result;
    int rc;

    if( dst->allocated < src1->len + src2->len ) {
        MP_GROW( dst, src1->len + src2->len - dst->allocated );
    }

    MP_INIT( &temp, 0 );
    MP_INIT( &result, 0 );

    for( cur = 0; cur < src2->len; cur++ ) {
        sc = src2->num[cur];
        if( sc != 1 ) {
            rc = mp_mulsc( &temp, src1, sc );
            if( rc != 0 ) goto done;
            rc = mp_shiftleft( &temp, &temp, cur * 32 );
        } else {
            rc = mp_shiftleft( &temp, src1, cur * 32 );
        }
        if( rc != 0 ) goto done;
        rc = mp_add( &result, &result, &temp );
        if( rc != 0 ) goto done;
    }

    MP_COPY( dst, &result );
    rc = MP_NO_ERR;

done:
    mp_free( &temp );
    mp_free( &result );

    return rc;
}

/* divide an mpnum by a scalar value; mode indicates ceiling or floor */
int mp_divsc( mpnum *dst, mpnum *src, uint32 scalar, int mode )
{
    uint64 dividend = 0;
    uint32 remainder = 0;
    uint32 i;

    if( scalar == 0 ) return MP_DIVIDE_BY_ZERO;
    if( scalar == 1 || src->len == 0 ) {
        if( dst != src ) MP_COPY( dst, src );
        return MP_NO_ERR;
    }
    if( dst->allocated < src->len ) {
        MP_GROW( dst, src->len - dst->allocated );
    }
    for( i = src->len - 1; ; i-- ) {
        dividend = (((uint64)remainder) << 32) | (uint64)src->num[i];
        dst->num[i] = (uint32)(dividend / scalar);
        remainder = (uint32)(dividend % scalar);
        if( i == 0 ) break;
    }
    dst->len = src->len;
    if( remainder > 0 && mode == CEILING ) {
        mp_addsc( dst, dst, 1 );
    }
    if( dst->num[src->len-1] == 0 ) dst->len--;
    return MP_NO_ERR;
}

/* This division algorithm is essentially the pen-and-paper long division
 * method.
 */
int mp_div( mpnum *qdst, mpnum *rdst, mpnum *x, mpnum *y )
{
    mpnum temp, temp2, q, r, mptmp1, mptmp2, mptmp3;
    uint32      n = x->len;
    uint32      t = y->len;
    uint32      i;
    int         rc = -1;
    uint64      tmp;
    uint32      shift = 0;

    MP_INIT( &q, 0 );
    if( q.allocated < x->len ) MP_GROW( &q, x->len - q.allocated );
    q.len = x->len;
    MP_INIT( &r, 0 );
    MP_INIT( &temp, 0 );
    MP_INIT( &temp2, 0 );
    i = 0;

    /* copy x into r */
    mp_copy( &r, x );

    /* check division by zero */
    if( mp_eq( &q, y ) ) {
        rc = MP_DIVIDE_BY_ZERO;
        goto done;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?