mp.c

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

C
1,082
字号
    }

    /* check if dividend is zero or x is less than y */
    if( mp_eq( &q, x ) || mp_lt( x, y ) ) {
        rc = MP_NO_ERR;
        goto done;
    }

    /* make sure n and t are true lengths */
    while( x->num[n-1] == 0 ) {
        n--;
        if( n == 0 ) goto done;
    }
    while( y->num[t-1] == 0 ) {
        t--;
        if( t == 0 ) goto done;
    }

    /* make sure first digit of divisor is >= base / 2 to ensure we get a
       good guess */
    shift = 31 - ((mp_bitsize( y ) - 1) % 32);
    if( ((mp_bitsize( &r ) + 1) % 32 - 1) + shift > 32 ) n++;
    mp_shiftleft( y, y, shift );
    mp_shiftleft( &r, &r, shift );

    /* step 2 */
    if( temp.allocated < n - t +1 ) MP_GROW( &temp, n - t - temp.allocated + 1 );
    temp.num[n-t] = 1;
    temp.len = n - t + 1;
    rc = mp_mul( &temp, &temp, y );
    if( rc != MP_NO_ERR ) goto done;
    for(;;) {
        if( mp_lt( &r, &temp ) ) break;
        q.num[n-t]++;
        mp_sub( &r, &r, &temp );
    }

    /* step 3 */
    for( i = n; i >= t + 1; i-- ) {
        /* 3.1 - make guess using two digits */
        if( r.num[i-1] == y->num[t-1] ) {
            q.num[i-t-1] = 0xFFFFFFFF;
        } else {
            tmp = ((uint64)(r.num[i-1])<<32  | (uint64)r.num[i-2]) / y->num[t-1];
            q.num[i-t-1] = (uint32) tmp;
        }
        /* 3.2 - improve guess using three digits */
        if( t > 1 ) {
            tmp = ((uint64)(r.num[i-1])<<32) | (uint64)r.num[i-2];
            MP_INIT( &mptmp1, tmp );
            MP_INIT( &mptmp3, 0 );
            mp_shiftleft( &mptmp1, &mptmp1, 32 );
            mp_addsc( &mptmp1, &mptmp1, r.num[i-3] );
            tmp = ((uint64)(y->num[t-1])<<32) | (uint64)y->num[t-2];
            MP_INIT( &mptmp2, tmp );
            mp_mulsc( &mptmp3, &mptmp2, q.num[i-t-1] );
            for(;;) {
                if( mp_lte( &mptmp3, &mptmp1 ) ) break;
                q.num[i-t-1]--;
                mp_sub( &mptmp3, &mptmp3, &mptmp2 );
            }
            mp_free( &mptmp1 );
            mp_free( &mptmp2 );
            mp_free( &mptmp3 );
        }
        /* 3.3 + 3.4 */
        mp_zero( &temp );
        mp_zero( &temp2 );
        temp.len = i - t;
        temp.num[i-t-1] = 1;
        rc = mp_mul( &temp, &temp, y );
        if( rc != MP_NO_ERR ) goto done;
        rc = mp_mulsc( &temp2, &temp, q.num[i-t-1] );
        if( rc != MP_NO_ERR ) goto done;
        if( mp_lt( &r, &temp2 ) ) {
            /* the guess was off by one */
            rc = mp_sub( &temp2, &temp2, &temp );
            if( rc != MP_NO_ERR ) goto done;
            q.num[i-t-1]--;
        }
        rc = mp_sub( &r, &r, &temp2 );
        if( rc != MP_NO_ERR ) goto done;
    }

    rc = MP_NO_ERR;
done:
    if( rc== MP_NO_ERR ) {
        mp_shiftright( y, y, shift );
        MP_COPY( qdst, &q );
        MP_COPY( rdst, &r );
        mp_shiftright( rdst, rdst, shift );
    }
    mp_free( &temp );
    mp_free( &temp2 );
    mp_free( &q );
    mp_free( &r );
    return rc;
}

/* sqaure src */
int mp_sqr( mpnum *dst, mpnum *src )
{
    return mp_mul( dst, src, src );
}

/* calculate src to the exp power using the binary method */
int mp_pow( mpnum *dst, mpnum *src, uint32 exp )
{
    mpnum temp;
    uint32 bit = 1;
    int rc;

    MP_INIT( &temp, 0 );
    MP_COPY( &temp, src );
    mp_free( dst );
    MP_INIT( dst, 1 );

    for( ;; ) {
        if( exp & bit ) {
            rc = mp_mul( dst, dst, &temp );
            if( rc != MP_NO_ERR ) goto done;
        }
        bit <<= 1;
        if( bit > exp ) break;
        rc = mp_sqr( &temp, &temp );
        if( rc != MP_NO_ERR ) goto done;
    }

    rc = MP_NO_ERR;

done:
    mp_free( &temp );

    return rc;
}

/* Do a binary rounding at the 'bit'th bit;
 * Round to nearest rounding mode is used and if there are two value equally
 * close to the number, it rounds to the even one. */
int mp_binround( mpnum *dst, mpnum *src, uint64 bit )
{
    uint32 bitval = two_to_the_31;
    uint32 unit = bit / 32;
    int isFirstBit = TRUE;
    int i;
    int round = 0;  /* -1 => down, 0 => even, 1 => up */
    uint32 valoffirstbit;
    uint32 firstunit = unit;
    bit = bit % 32;

    if( dst->allocated < src->len + 1 ) {
        MP_GROW( dst, src->len + 1 - dst->allocated );
    }

    MP_COPY( dst, src );

    /* handle special case where bit is 0 */
    if( unit == 0 && bit == 0 ) {
        return MP_NO_ERR;
    }

    for( i = 31; i > bit; i-- ) {
        bitval >>= 1;
    }
    valoffirstbit = bitval;

    /* determine rounding */
    for(;;) {
        /* decrement bit */
        if( bitval == 1 ) {
            if( unit == 0 ) break;
            unit--;
            bitval = two_to_the_31;
        } else {
            bitval >>= 1;
        }

        if( isFirstBit ) {
            if( !(src->num[unit] & bitval) ) {
                round = -1;  /* round down */
                break;
            }
            isFirstBit = FALSE;
        } else {
            if( src->num[unit] & bitval ) {
                round = 1; /* round up */
                break;
            }
        }
    }

    /* add 1 if necessary */
    if( (round == 1) || (round == 0 && (src->num[firstunit] & valoffirstbit) ) ) {
        mpnum temp;
        MP_INIT( &temp, 0 );
        if( firstunit >= temp.allocated ) {
            MP_GROW( &temp, firstunit - temp.allocated + 1);
        }
        mp_zero( &temp );
        temp.num[firstunit] = valoffirstbit;
        temp.len = firstunit + 1;
        mp_add( dst, dst, &temp );
        mp_free( &temp );
    }

    /* truncate */
    dst->num[firstunit] = dst->num[firstunit] & (ULONG_MAX - (valoffirstbit - 1));
    for( unit = firstunit;; unit--) {
        if( unit == 0 ) break;
        dst->num[unit-1] = 0;
    }

    return MP_NO_ERR;
}

/* Convert src to a real 4 byte number using mp_binround to round to the
 * nearest value. */
int mp_tofloat( uint8 *dst, mpnum *src )
{
    uint32 firstunit = src->len - 1;
    uint8  firstbit = 31;
    uint32 bit = two_to_the_31;
    uint64 exp;
    uint8 *temp;
    uint64 temp2;
    uint64 sig = 0;
    uint32 left = 0;
    uint8 i;
    mpnum rounded_num;

    memset( dst, 0, 4 );

    /* find first non-zero bit */
    if( src->len == 0 ) {
        return MP_NO_ERR;
    }
    for( ;; firstunit-- ) {
        if( src->num[firstunit] > 0 ) {
            break;
        }
        if( firstunit == 0 ) return MP_NO_ERR; /* src = 0 */
    }
    for( ;; firstbit-- ) {
        if( src->num[firstunit] & bit ) {
            break;
        }
        bit >>= 1;
    }

    /* round this number so that we have 24 significant bits */
    MP_INIT( &rounded_num, 0 );
    MP_COPY( &rounded_num, src );
    if( firstunit * 32 + firstbit > 23 ) {
        mp_binround( &rounded_num, &rounded_num, firstunit * 32 + firstbit - 23 );
        /* if it rounded up, the first bit may be up one */
        if( firstbit == 31 ) {
            if( rounded_num.num[firstunit+1] > 0 ) {
                firstunit++;
                firstbit = 0;
            }
        } else {
            if( rounded_num.num[firstunit] & (bit * 2) ) {
                firstbit++;
            }
        }
    }

    /* generate exponent */
    exp = (uint64)firstunit * 32 + firstbit + float_bias;
    if( exp > 255 ) exp = 255;
    temp = (uint8*)&exp;
    dst[3] = temp[0]>>1;
    dst[2] = temp[0]<<7;
    if( exp == 255 ) {
        /* too large => use +infinity */
        mp_free( &rounded_num );
        return MP_LOSS_OF_PRECISION;
    }

    /* generate significand */
    if( firstbit > 22 ) {
        left = 0;
        sig = (uint64)rounded_num.num[firstunit] >> (firstbit - 23);
    } else {
        left = 23 - firstbit;
        sig = (uint64)rounded_num.num[firstunit] << left;
    }
    if( firstunit > 0 && left > 0 ) {
        temp2 = (uint64)rounded_num.num[firstunit-1] >> (32-left);
        sig = sig | temp2;
        left = 0;
    }
    temp = (uint8*)&sig;
    dst[2] = dst[2] | (temp[2] & 127);
    for( i = 0; i < 2; i++ ) {
        dst[i] = temp[i];
    }

    mp_free( &rounded_num );
    return MP_NO_ERR;
}

/* Convert src to a real 8 byte number using mp_binround to round to the
 * nearest value. */
int mp_todouble( uint8 *dst, mpnum *src )
{
    uint32 firstunit = src->len - 1;
    uint8  firstbit = 31;
    uint32 bit = two_to_the_31;
    uint64 exp;
    uint8 *temp;
    uint64 temp2;
    uint64 sig = 0;
    uint32 left = 0;
    uint8 i;
    mpnum rounded_num;

    memset( dst, 0, 8 );
    if( src->len == 0 ) {
        return MP_NO_ERR;
    }
    for( ;; firstunit-- ) {
        if( src->num[firstunit] > 0 ) {
            break;
        }
        if( firstunit == 0 ) return MP_NO_ERR; /* src = 0 */
    }
    for( ;; firstbit-- ) {
        if( src->num[firstunit] & bit ) {
            break;
        }
        bit >>= 1;
    }

    /* round this number so that we have 53 significant bits */
    MP_INIT( &rounded_num, 0 );
    MP_COPY( &rounded_num, src );
    if( firstunit * 32 + firstbit > 52 ) {
        mp_binround( &rounded_num, &rounded_num, firstunit * 32 + firstbit - 52 );
        /* if it rounded up, the first bit may be up one */
        if( firstbit == 31 ) {
            if( rounded_num.num[firstunit+1] > 0 ) {
                firstunit++;
                firstbit = 0;
            }
        } else {
            if( rounded_num.num[firstunit] & (bit * 2) ) {
                firstbit++;
            }
        }
    }

    /* generate exponent */
    exp = (uint64)firstunit * 32 + firstbit + double_bias;
    if( exp > 2047 ) exp = 2047;
    temp = (uint8*)&exp;
    dst[7] = temp[1] << 4;
    dst[7] = dst[7] | ((temp[0] & 240) >> 4);
    dst[6] = (temp[0] & 15) << 4;
    if( exp == 2047 ) {
        /* too large => use +infinity */
        mp_free( &rounded_num );
        return MP_LOSS_OF_PRECISION;
    }

    /* generate significand */
    left = 52 - firstbit;
    sig = (uint64)rounded_num.num[firstunit] << left;
    if( firstunit > 0 ) {
        if( left > 32 ) {
            left -= 32;
            temp2 = (uint64)rounded_num.num[firstunit-1] << left;
            sig = sig | temp2;
        } else if( left < 32 ) {
            temp2 = (uint64)rounded_num.num[firstunit-1] >> (32-left);
            sig = sig | temp2;
            left = 0;
        } else {
            temp2 = (uint64)rounded_num.num[firstunit-1];
            sig = sig | temp2;
            left = 0;
        }
    }
    if( left > 0 && firstunit > 1 ) {
        temp2 = (uint64)rounded_num.num[firstunit-2] >> (32-left);
        sig = sig | temp2;
    }
    temp = (uint8*)&sig;
    for( i = 0; i < 6; i++ ) {
        dst[i] = temp[i];
    }
    dst[6] = dst[6] | (temp[6] & 15);

    mp_free( &rounded_num );
    return MP_NO_ERR;
}

/* Convert src to a real 10 byte number using mp_binround to round to the
 * nearest value. */
int mp_toextended( uint8 *dst, mpnum *src )
{
    uint32 firstunit = src->len - 1;
    uint8  firstbit = 31;
    uint32 bit = two_to_the_31;
    uint64 exp;
    uint8 *temp;
    uint64 temp2;
    uint64 sig = 0;
    uint32 left = 0;
    uint8 i;
    mpnum rounded_num;

    memset( dst, 0, 10 );

    /* find first non-zero bit */
    if( src->len == 0 ) {
        return MP_NO_ERR;
    }
    for( ;; firstunit-- ) {
        if( src->num[firstunit] > 0 ) {
            break;
        }
        if( firstunit == 0 ) return MP_NO_ERR; /* src = 0 */
    }
    for( ;; firstbit-- ) {
        if( src->num[firstunit] & bit ) {
            break;
        }
        bit >>= 1;
    }

    /* round this number so that we have 64 significant bits */
    MP_INIT( &rounded_num, 0 );
    MP_COPY( &rounded_num, src );
    if( firstunit * 32 + firstbit > 63 ) {
        mp_binround( &rounded_num, &rounded_num, firstunit * 32 + firstbit - 63 );
        /* if it rounded up, the first bit may be up one */
        if( firstbit == 31 ) {
            if( rounded_num.num[firstunit+1] > 0 ) {
                firstunit++;
                firstbit = 0;
            }
        } else {
            if( rounded_num.num[firstunit] & (bit * 2) ) {
                firstbit++;
            }
        }
    }

    /* generate exponent */
    exp = (uint64)firstunit * 32 + firstbit + extended_bias;
    if( exp > 32767 ) exp = 32767;
    temp = (uint8*)&exp;
    dst[9] = temp[1];
    dst[8] = temp[0];
    if( exp == 32767 ) {
        /* too large => use +infinity */
        mp_free( &rounded_num );
        return MP_LOSS_OF_PRECISION;
    }

    /* generate significand */
    left = 63 - firstbit;
    sig = (uint64)rounded_num.num[firstunit] << left;
    if( firstunit > 0 ) {
        if( left > 32 ) {
            left -= 32;
            temp2 = (uint64)rounded_num.num[firstunit-1] << left;
            sig = sig | temp2;
        } else if( left < 32 ) {
            temp2 = (uint64)rounded_num.num[firstunit-1] >> (32-left);
            sig = sig | temp2;
            left = 0;
        } else {
            temp2 = (uint64)rounded_num.num[firstunit-1];
            sig = sig | temp2;
            left = 0;
        }
    }
    if( left > 0 && firstunit > 1 ) {
        temp2 = (uint64)rounded_num.num[firstunit-2] >> (32-left);
        sig = sig | temp2;
    }
    temp = (uint8*)&sig;
    for( i = 0; i < 8; i++ ) {
        dst[i] = temp[i];
    }

    mp_free( &rounded_num );
    return MP_NO_ERR;
}

int mp_touint64( uint64 *dst, mpnum *src )
{
    mpnum limit;
    uint32 *temp = (uint32*)dst;

    MP_INIT( &limit, (uint64)0xffffffffffffffff );
    if( mp_gt( src, &limit ) ) {
        temp[0] = limit.num[0];
        temp[1] = limit.num[1];
        mp_free( &limit );
        return MP_TOO_LARGE;
    }
    temp[0] = src->num[0];
    if( src->len > 1 ) {
        temp[1] = src->num[1];
    } else {
        temp[1] = 0;
    }
    mp_free( &limit );
    return MP_NO_ERR;
}

/* returns size of num in bits */
int mp_bitsize( mpnum *num )
{
    uint32 i = num->len - 1;
    uint32 j = 32;
    uint32 bit = two_to_the_31;

    /* find first non-zero bit */
    if( num->len == 0 ) {
        return 0;
    }
    for( ;; i-- ) {
        if( num->num[i] > 0 ) {
            break;
        }
        if( i == 0 ) return 0; /* src = 0 */
    }
    for( ;; j-- ) {
        if( num->num[i] & bit ) {
            break;
        }
        bit >>= 1;
    }
    return i * 32 + j;
}

⌨️ 快捷键说明

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