asciifp.c

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

C
568
字号
/****************************************************************************
*
*                            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 defines functions for converting between floating point numbers
 * and ascii strings.  It uses multiply precions integers to get accurate
 * results.
 */
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include "mp.h"
#include "fp.h"

/* real number types */
#define FLOAT                   1
#define DOUBLE                  2
#define EXTENDED                3

#define max_exp                 32768

#define ASCII_POS_ZERO          "0"
#define ASCII_NEG_ZERO          "0"
#define ASCII_POS_INF           "Infinity"
#define ASCII_NEG_INF           "-Infinity"
#define ASCII_NAN               "NaN"

/* Remove trailing zeroes at the end of the mantissa part of the number to
 * speed up the calculations. */
int RemoveTrailingZeros( char *ascii )
{
    int i;

    for( i = 0; ; i++ ) {
        if( ascii[i] == 'e' || ascii[i] == 'E' || ascii[i] == '\0' ) break;
    }
    for( i--; i > 0; i-- ) {
        if( ascii[i] == '0' ) {
            ascii[i] = '_';
        } else if( ascii[i] != '.' ) {
            break;
        }
    }
    return 0;
}

/* parse ascii string into the mantissa f and the exponent e */
/* note: this function assumes a positive value */
int ParseAscii( char *ascii, mpnum *f, long *e )
{
    int readingExponent = FALSE;
    int expIsNeg = FALSE;
    int behindDecimal = FALSE;
    uint32 digitsBehindDecimal = 0;
    int rc;
    int sigexp = 0;
    *e = 0;
    mp_zero( f );

    RemoveTrailingZeros( ascii );

    for( ; *ascii != '\0'; ascii++ ) {
        if( *ascii == '.' ) {
            /* decimal point */
            behindDecimal = TRUE;
        } else if( *ascii == 'e' || *ascii == 'E' ) {
            /* exponent */
            if( ascii[1] == '-' ) {
                expIsNeg = TRUE;
                ascii++;
            } else if( ascii[1] == '+' ) {
                ascii++;
            }
            readingExponent = TRUE;
        } else if( *ascii >= '0' && *ascii <= '9' ) {
            if( readingExponent ) {
                if( *e <= max_exp ) {
                    *e = *e * 10 + (long)(*ascii - '0');
                }
            } else {
                /* number */
                rc = mp_mulsc( f, f, 10 );
                if( rc != MP_NO_ERR ) return rc;
                rc = mp_addsc( f, f, (uint32)(*ascii - '0') );
                if( rc != MP_NO_ERR ) return rc;
                if( behindDecimal ) {
                    digitsBehindDecimal++;
                }
            }
        } else if( *ascii == '_' ) {
            *ascii = '0';
            if( !behindDecimal ) {
                sigexp++;
            }
        } else {
            /* unrecognized character */
        }
    }
    if( expIsNeg ) *e = -*e;
    *e -= digitsBehindDecimal;
    *e += sigexp;

    return 0;
}

/* convert ascii string to a real number of type type */
int a2r( void *dst, char *ascii, int type )
{
    int isNeg = FALSE;
    char exp_real[10];
    mpnum f;
    mpnum exp;
    long e = 0;
    int rc;
    mpnum zero;
    int isZero;

    if( type != FLOAT && type != DOUBLE && type != EXTENDED ) return -1;

    /* check for sign */
    if( *ascii == '-' ) {
        isNeg = TRUE;
        ascii++;
    } else if( *ascii == '+' ) {
        ascii++;
    }

    rc = mp_init( &f, 0 );
    if( rc != MP_NO_ERR ) return rc;
    rc = mp_init( &exp, 10 );
    if( rc != MP_NO_ERR ) return rc;
    rc = mp_init( &zero, 0 );
    if( rc != MP_NO_ERR ) return rc;

    /* parse ascii into significand and exponent */
    ParseAscii( ascii, &f, &e );
    isZero = mp_eq( &f, &zero );
    mp_free( &zero );

    /* calculate 10^abs(e) */
    if( e > 0 ) {
        rc = mp_pow( &exp, &exp, (uint32)e );
    } else if( e < 0 ) {
        rc = mp_pow( &exp, &exp, (uint32)(-e) );
    }
    if( rc != MP_NO_ERR ) return rc;

    /* combine significand and exponent as reals */
    if( type == FLOAT ) {
        if( e > 0 ) {
            mp_mul( &f, &f, &exp );
            mp_tofloat( dst, &f );
        } else if( e < 0 ) {
            char temp[10];
            mp_toextended( temp, &f );
            mp_toextended( exp_real, &exp );
            ediv( temp, temp, exp_real );
            e2f( dst, temp );
        } else {
            mp_tofloat( dst, &f );
        }
    } else if( type == DOUBLE ) {
        if( e > 0 ) {
            mp_mul( &f, &f, &exp );
            mp_todouble( dst, &f );
        } else if( e < 0 ) {
            char temp[10];
            mp_toextended( temp, &f );
            mp_toextended( exp_real, &exp );
            ediv( temp, temp, exp_real );
            e2d( dst, temp );
        } else {
            mp_todouble( dst, &f );
        }
    } else {
        mp_toextended( dst, &f );
        mp_toextended( exp_real, &exp );
        if( e > 0 ) {
            emul( dst, dst, exp_real );
        } else if( e < 0 ) {
            ediv( dst, dst, exp_real );
        }
    }

    /* add sign */
    if( isNeg ) {
        if( type == FLOAT ) fneg( dst, dst );
        else if( type == DOUBLE ) dneg( dst, dst );
        else eneg( dst, dst );
    }
    mp_free( &f );
    mp_free( &exp );

    /* check if numbers are too large or too small */
    if( type == FLOAT ) {
        if( !isZero && ( f_isPosZero(dst) || f_isNegZero(dst) ) ) {
            return VALUE_TOO_SMALL;
        }
        if( f_isPosInf(dst) || f_isNegInf(dst) ) {
            return VALUE_TOO_LARGE;
        }
    } else if( type == DOUBLE ) {
        if( !isZero && ( d_isPosZero(dst) || d_isNegZero(dst) ) ) {
            return VALUE_TOO_SMALL;
        }
        if( d_isPosInf(dst) || d_isNegInf(dst) ) {
            return VALUE_TOO_LARGE;
        }
    }

    return 0;
}

int a2e( char *ext, char *ascii )
{
    return( a2r( ext, ascii, EXTENDED ) );
}

int a2d( double *dbl, char *ascii )
{
    return( a2r( dbl, ascii, DOUBLE ) );
}

int a2f( float *flt, char *ascii )
{
    return( a2r( flt, ascii, FLOAT ) );
}

/* Generate String
 *
 * This function uses an algorithm found in an essay entitled "How to Print
 * Floating Point Numbers Accurately" by Guy L. Steele Jr and Jon L White.
 * It takes as input a binary floating point number and generates a string
 * of decimal digits which represent the most significant bits of a decimal
 * representation of the number.  H represents the power of ten of the most
 * significant digit, and N represents the power of ten of the least signi-
 * ficant digit.  For example, for string "123456", H = 1, N = -4, the
 * decimal number is 12.3456.
 *
 * The resulting decimal number is not the closest possible representation
 * of the binary number.  Instead, it is the number of minimal length such
 * that converting the number to a binary floating point yields the original
 * binary value.  One advantage of this is that the algorithm will return
 * "1.3" for 1.3 instead of something like "1.299999952316284". (1.3 cannot
 * be stored exactly in binary form.)
 *
 * Note: D is assumed to be large enough to hold the whole string.  In the
 * worst case for a double, it will require 18 bytes.
 */
int GenerateString( char *D, void *src, int type, int *N, int *H )
{
    uint64 temp;
    int e;
    mpnum f, R, S, Mp, Mn, U, mptemp, mptemp2;
    int k = 0, p;

⌨️ 快捷键说明

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