cfdiv.c

来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 266 行

C
266
字号
/****************************************************************************
*
*                            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!
*
****************************************************************************/


#include <stdlib.h>
#include <limits.h>

#include "watcom.h"
#include "cfloat.h"


static void efInit( char ue[] )
{
    int     i;

    for( i = 0; i < CF_MAX_PREC; i++ ) {
        ue[ i ] = 0;
    }
}

static int efGet( cfloat *u, char ue[], int i )
{
    if( i >= u->len ) {
        return( ue[ i - u->len ] );
    } else {
        return( u->mant[ i ] - '0' );
    }
}

static void efSet( cfloat *u, char ue[], int i, int val )
{
    if( i >= u->len ) {
        ue[ i - u->len ] = val;
    } else {
        u->mant[ i ] = val + '0';
    }
}

static cfloat *scalarMultiply( cfloat *f, int s )
{
    cfloat      *res;
    div_t        d;
    int          i;

    res = CFAlloc( f->len + 1 );

    res->len  = f->len + 1;
    res->exp  = f->exp;
    res->sign = f->sign;

    d.quot = 0;

    for( i = f->len - 1; i >= 0; i-- ) {
        d = div( s * CFAccess( f, i ) + d.quot, 10 );
        CFDeposit( res, i + 1, d.rem );
    }
    CFDeposit( res, 0, d.quot );
    return( res );
}

static void expandCF( cfloat **f, int scale )
{
    cfloat      *new;
    int          l;

    new = CFAlloc( scale + (*f)->len );

    l   = (*f)->len;
    while( l > 0 ) {
        l--;
        new->mant[ l ] = (*f)->mant[ l ];
    }

    l = scale + (*f)->len;
    while( (*f)->len < l ) {
        new->mant[ (*f)->len ] = '0';
        (*f)->len++;
    }

    new->mant[ (*f)->len ] = '\0';
    new->sign = (*f)->sign;
    new->exp  = (*f)->exp;
    new->len  = l;

    CFFree( *f );

    *f = new;
}

static void roundupCF( cfloat *f )
{
    int     i;

    for( i = f->len - 1; i >= 0; i-- ) {
        if( f->mant[ i ] == '9' ) {
            f->mant[ i ] = '0';
        } else {
            f->mant[ i ] += 1;
            return;
        }
    }

    f->mant[ 0 ] = '1';
    f->exp += 1;
}

/*
 * CFDiv:  Computes  op1 / op2
 */
extern  cfloat  *CFDiv( cfloat *op1, cfloat *op2 ) {
    cfloat         *result;
    cfloat         *u, *v;
    int             i, j, qa, ua, va, v1, cy, scale;
    div_t           d;
    char            ue[ CF_MAX_PREC ];

    if( ! op2->sign ) {                         // Attempt to divide by zero.
        result = CFAlloc( 1 );
        result->mant[0] = '1';
        result->sign    = 1;
        result->exp     = CF_ERR_EXP;           // Return error-type.
        return( result );
    }

    efInit( ue );                               // Initialize extended float.

    result  = CFAlloc( CF_MAX_PREC );           // Allocate mem. for result.

    result->sign = op1->sign * op2->sign;       // Set sign of result.
    result->exp  = op1->exp - op2->exp + 1;     // Set exponent of result.
    result->len  = 0;

    if( CFAccess( op2, 0 ) < 5 ) {
        scale = 10 / (CFAccess( op2, 0 ) + 1);
    } else {
        scale = 1;
    }

    u = scalarMultiply( op1, scale );           // Extra digit added.
    v = scalarMultiply( op2, scale );           // Extra digit added.

    if( v->len < 3 ) {                          // Divisor must have at least
        expandCF( &v, 1 );                      // two digits (ignore leading 0)
    }
    if( u->len <= v->len ) {                    // Dividend must have more
        expandCF( &u, v->len - u->len + 1 );    // digits than divisor.
    }

    /*
     * We now use the classical division algorithm described in Knuth,
     * _Seminumerical_Algorithms_, section 4.3.1.
     *
     * The following implementation uses base 10, and the initial approximation
     * is made by taking qa = floor( uj.uj1.uj2 / v1.v2 ) instead of simply
     * floor( uj.uj1 / v1 ).  According to Knuth, this initial approximation
     * is always greater than the real quotient digit, and off by at most two.
     */

    v1 = CFAccess( v, 1 );
    va = 10 * v1 + CFAccess( v, 2 );

    for( j = 0; j <= CF_MAX_PREC; j++ ) {

        // Make initial approximation of the quotient digit.

        if( v1 == efGet( u, ue, j ) ) {
            qa = 9;
        } else {
            ua = efGet(u,ue,j) * 100 + efGet(u,ue,j+1) * 10 + efGet(u,ue,j+2);
            qa = ua / va;
        }

        /*
         * Replace (uj.uj1...ujn) with (uj.uj1..ujn) - qa * (v1.v2..vn)
         */

        cy = 0;
        for( i = v->len - 1; i >= 0; i-- ) {
            d = div( efGet( u, ue, i + j ) - qa * CFAccess( v, i ) + cy, 10 );
            if( d.rem < 0 ) {
                d.rem  += 10;
                d.quot -= 1;
            }
            cy = d.quot;
            efSet( u, ue, i + j, d.rem );
        }

        /*
         * The above subtraction resulted in a negative number.  So qa was
         * in fact off by one.  Correct that, and add back to correct
         * (uj.uj1...ujn).
         */

        if( cy ) {
            qa--;
            cy = 0;
            for( i = v->len - 1; i >= 0; i-- ) {
                d = div( efGet( u, ue, i + j ) + CFAccess( v, i ) + cy, 10 );
                efSet( u, ue, i + j, d.rem );
                cy = d.quot;
            }
        }

        /*
         * Set the real quotient digit, and do some rounding when maximum
         * precision is reached.  [Rounding is unbiased.]
         */

        if( j < CF_MAX_PREC ) {
            CFDeposit( result, j, qa );
            result->len++;
        } else {
            if( qa > 5 ) {
                roundupCF( result );
            } else if( qa == 5 ) {
                cy = 0;
                for( i = v->len - 1; i >= 0; i-- ) {
                    if( efGet( u, ue, i + j ) != 0 ) {
                        cy = 1;
                        break;
                    }
                }
                if( cy ) {
                    roundupCF( result );
                } else if( CFAccess( result, CF_MAX_PREC - 1 ) % 2 != 0 ) {
                    roundupCF( result );
                }
            }
        }
    }

    CFFree( u );                            // Clean up the mess we made.
    CFFree( v );
    CFClean( result );                      // Clean up the number we made.

    return( result );
}

⌨️ 快捷键说明

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