ldcvt.c

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

C
768
字号
/****************************************************************************
*
*                            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:  Conversion of floating-point values to strings.
*
****************************************************************************/


#include "variety.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
#include "xfloat.h"

#define NDIG            8

// this is defined in float.h
#if LDBL_MAX_10_EXP == 308
#undef LDBL_MAX_10_EXP
#define LDBL_MAX_10_EXP 4096
#else
#error LDBL_MAX_10_EXP has changed from 308
#endif

/* We must use more than DBL_DIG/LDBL_DIG, otherwise we lose precision! */
#define DBL_CVT_DIGITS      20
#define LDBL_CVT_DIGITS     23

// this manufactures a value from macros defined in float.h
#define __local_glue( __x, __y ) __x ## __y
#define local_glue( __x, __y ) __local_glue( __x, __y )
#define ONE_TO_DBL_MAX_10_EXP local_glue( 1e, DBL_MAX_10_EXP )
#define ONE_TO_DBL_MIN_10_EXP 1e307


#if defined( __386__ )
 char _WCNEAR *Fmt8Digits( unsigned long value, char *p );
 #pragma aux    Fmt8Digits = \
                "       push    ecx"\
                "       push    edx"\
                "       call    fmt8"\
                "       jmp     short L4"\
                "fmt8:  mov     ecx,10000"\
                "       sub     edx,edx"\
                "       cmp     eax,ecx"\
                "       xchg    eax,edx"\
                "       jb      short L1"\
                "       xchg    eax,edx"\
                "       div     ecx"\
                "L1:    push    edx"\
                "       call    fmt4"\
                "       pop     eax"\
                "fmt4:  mov     ecx,100"\
                "       sub     edx,edx"\
                "       cmp     eax,ecx"\
                "       xchg    eax,edx"\
                "       jb      short L2"\
                "       xchg    eax,edx"\
                "       div     cx"\
                "L2:    push    edx"\
                "       call    fmt2"\
                "       pop     eax"\
                "fmt2:  mov     cl,10"\
                "       cmp     al,cl"\
                "       xchg    al,ah"\
                "       jb      short L3"\
                "       xchg    al,ah"\
                "       div     cl"\
                "L3:    add     ah,'0'"\
                "       add     al,'0'"\
                "       mov     [ebx],al"\
                "       inc     ebx"\
                "       mov     [ebx],ah"\
                "       inc     ebx"\
                "       ret"\
                "L4:    pop     edx"\
                "       pop     ecx"\
                "       mov     al,0"\
                "       mov     [ebx],al"\
                parm caller [eax] [ebx] value [ebx];
#elif defined( M_I86 )
 char _WCNEAR *Fmt8Digits( unsigned long value, char *p );
 #pragma aux    Fmt8Digits = \
                "       push    cx"\
                "       call    fmt8"\
                "       jmp     short L4"\
                "fmt8:  mov     cx,10000"\
                "       div     cx"\
                "       push    dx"\
                "       call    fmt4"\
                "       pop     ax"\
                "fmt4:  mov     cx,100"\
                "       sub     dx,dx"\
                "       cmp     ax,cx"\
                "       xchg    ax,dx"\
                "       jb      short L2"\
                "       xchg    ax,dx"\
                "       div     cx"\
                "L2:    push    dx"\
                "       call    fmt2"\
                "       pop     ax"\
                "fmt2:  mov     cl,10"\
                "       cmp     al,cl"\
                "       xchg    al,ah"\
                "       jb      short L3"\
                "       xchg    al,ah"\
                "       div     cl"\
                "L3:    add     ax,3030h"\
                "       mov     ss:[bx],ax"\
                "       inc     bx"\
                "       inc     bx"\
                "       ret"\
                "L4:    pop     cx"\
                "       mov     al,0"\
                "       mov     ss:[bx],al"\
                parm caller [dx ax] [bx] value [bx];
#else
static unsigned long IntPow10[] = {
    1,
    10,
    100,
    1000,
    10000,
    100000,
    1000000,
    10000000,
};

static char _WCNEAR *Fmt8Digits( unsigned long value, char *p )
{
    int                 i;
    int                 digit;
    unsigned long       pow10;

    for( i = NDIG - 1; i != 0; i-- ) {
        digit = '0';
        pow10 = IntPow10[i];
        while( value >= pow10 ) {
            value -= pow10;
            ++digit;
        }
        *p++ = digit;
    }
    *p++ = value + '0';
    *p = '\0';
    return( p );
}
#endif

#ifdef _LONG_DOUBLE_
// Intel supports long double
#define E8_EXP          0x4019
#define E8_HIGH         0xBEBC2000
#define E8_LOW          0x00000000
#define E16_EXP         0x4034
#define E16_HIGH        0x8E1BC9BF
#define E16_LOW         0x04000000

static long_double LDPowTable[] = {
    { 0x00000000, 0xA0000000, 0x4002 }, // 1e1L
    { 0x00000000, 0xC8000000, 0x4005 }, // 1e2L
    { 0x00000000, 0x9C400000, 0x400C }, // 1e4L
    { 0x00000000, 0xBEBC2000, 0x4019 }, // 1e8L
    { 0x04000000, 0x8E1BC9BF, 0x4034 }, // 1e16L
    { 0x2B70B59E, 0x9DC5ADA8, 0x4069 }, // 1e32L
    { 0xFFCFA6D5, 0xC2781F49, 0x40D3 }, // 1e64L
    { 0x80E98CE0, 0x93BA47C9, 0x41A8 }, // 1e128L
    { 0x9DF9DE8E, 0xAA7EEBFB, 0x4351 }, // 1e256L
    { 0xA60E91C7, 0xE319A0AE, 0x46A3 }, // 1e512L
    { 0x81750C17, 0xC9767586, 0x4D48 }, // 1e1024L
    { 0xC53D5DE5, 0x9E8b3B5D, 0x5A92 }, // 1e2048L
    { 0x8A20979B, 0xC4605202, 0x7525 }, // 1e4096L
    { 0x00000000, 0x80000000, 0x7FFF }, // infinity
};

static void CalcScaleFactor( long_double _WCNEAR *factor, int n )
{
    long_double *pow;
    long_double tmp;

    if( n >= 8192 ) {
        n = 8192;               // set to infinity multiplier
    }
    for( pow = LDPowTable; n > 0; n >>= 1, ++pow ) {
        if( n & 1 ) {
            tmp.exponent  = pow->exponent;
            tmp.high_word = pow->high_word;
            tmp.low_word  = pow->low_word;
            __FLDM( factor, (long_double _WCNEAR *)&tmp, factor );
        }
    }
}

static void _do_LDScale10x( long_double _WCNEAR *ld, int scale )
{
    long_double factor;

    if( scale != 0 ) {
#if defined( _LONG_DOUBLE_ ) && defined( __FPI__ )
        unsigned short _8087cw = __Get87CW();
        __Set87CW( _8087cw | _PC_64 ); // make sure extended precision is on
#endif
        factor.exponent  = 0x3FFF;              // set factor = 1.0
        factor.high_word = 0x80000000;
        factor.low_word  = 0x00000000;
        if( scale < 0 ) {
            CalcScaleFactor( (long_double _WCNEAR *)&factor, -scale );
            __FLDD( ld, (long_double _WCNEAR *)&factor, ld );
        } else {
            CalcScaleFactor( (long_double _WCNEAR *)&factor, scale );
            __FLDM( ld, (long_double _WCNEAR *)&factor, ld );
        }
#if defined( _LONG_DOUBLE_ ) && defined( __FPI__ )
        __Set87CW( _8087cw );       // restore control word
#endif
    }
}

void _LDScale10x( long_double _WCNEAR *ld, int scale )
{
    if( scale > LDBL_MAX_10_EXP ) {
        _do_LDScale10x( ld, LDBL_MAX_10_EXP );
        scale -= LDBL_MAX_10_EXP;
    } else if( scale < -LDBL_MAX_10_EXP ) {
        _do_LDScale10x( ld, -LDBL_MAX_10_EXP );
        scale += LDBL_MAX_10_EXP;
    }
    _do_LDScale10x( ld, scale );
}

#else           /* 'long double' is same as 'double' */

static double Pow10Table[] = {
    1e1, 1e2, 1e4, 1e8, 1e16, 1e32, 1e64, 1e128, 1e256,
};

void _LDScale10x( long_double *ld, int scale )
{
    double      factor;
    double      *pow;
    int         n;

    if( scale != 0 ) {
        n = scale;
        if( scale < 0 ) n = -n;
        if( n > DBL_MAX_10_EXP ) {
            if( scale < 0 ) {
                ld->value /= ONE_TO_DBL_MIN_10_EXP;
                n += DBL_MIN_10_EXP;
            } else {
                ld->value *= ONE_TO_DBL_MAX_10_EXP;
                n -= DBL_MAX_10_EXP;
            }
        }
        factor = 1.0;
        for( pow = Pow10Table; n > 0; n >>= 1, ++pow ) {
            if( n & 1 ) {
                factor *= *pow;
            }
        }
        if( scale < 0 ) {
            ld->value /= factor;
        } else {
            ld->value *= factor;
        }
    }
}

#endif

static void DoFFormat( CVT_INFO *cvt, char *p, int nsig, int xexp, char *buf )
{
    int         i;
    int         ndigits;

    ndigits = cvt->ndigits;
    ++xexp;
    i = 0;
    if( cvt->flags & G_FMT ) {
        if( nsig < ndigits && !(cvt->flags & F_DOT) ) {
            ndigits = nsig;
        }
        ndigits -= xexp;
        if( ndigits < 0 ) {
            ndigits = 0;
        }
    }
    if( xexp <= 0 ) {   // digits only to right of '.'
        if( !(cvt->flags & F_CVT) ) {
            buf[i++] = '0';
            if( ndigits > 0 || (cvt->flags & F_DOT) ) {
                buf[i++] = '.';
            }
        }
        cvt->n1 = i;
        if( ndigits < -xexp ) {
            xexp = - ndigits;
        }
        cvt->decimal_place = xexp;
        cvt->nz1 = -xexp;
//      for( n = -xexp; n > 0; --n ) buf[i++] = '0';
        ndigits += xexp;
        if( ndigits < nsig ) {
            nsig = ndigits;
        }
        memcpy( &buf[i], p, nsig );
        i += nsig;
        cvt->n2 = nsig;
        cvt->nz2 = ndigits - nsig;
//      for( n = ndigits - nsig; n > 0; --n ) buf[i++] = '0';
    } else if( nsig < xexp ) {  // zeros before '.'
        memcpy( buf, p, nsig );
        i += nsig;
        cvt->n1 = nsig;
        cvt->nz1 = xexp - nsig;
        cvt->decimal_place = xexp;
//      for( n = xexp - nsig; n > 0; --n ) buf[i++] = '0';
        if( !(cvt->flags & F_CVT) ) {
            if( ndigits > 0 || (cvt->flags & F_DOT) ) {
                buf[i++] = '.';
                cvt->n2 = 1;
            }
        }
        cvt->nz2 = ndigits;
//      for( n = ndigits; n > 0; --n ) buf[i++] = '0';
    } else {                    // enough digits before '.'
        memcpy( buf, p, xexp );
        cvt->decimal_place = xexp;
        i += xexp;
        nsig -= xexp;
        if( !(cvt->flags & F_CVT) ) {
            if( ndigits > 0 || (cvt->flags & F_DOT) ) {
                buf[i++] = '.';
            }
        } else if( buf[0] == '0' ) {    // ecvt or fcvt with 0.0
            cvt->decimal_place = 0;
        }
        if( ndigits < nsig ) {
            nsig = ndigits;
        }
        memcpy( &buf[i], p + xexp, nsig );
        i += nsig;
        cvt->n1 = i;
        cvt->nz1 = ndigits - nsig;
//      for( n = ndigits - nsig; n > 0; --n ) buf[i++] = '0';
    }
    buf[i] = '\0';
}

static void DoEFormat( CVT_INFO *cvt, char *p, int nsig, int xexp, char *buf )
{
    int         i;
    int         n;
    int         ndigits;        // number of digits after decimal place
    int         width;

    ndigits = cvt->ndigits;
    if( cvt->scale <= 0 ) {
        ndigits += cvt->scale;  // decrease number of digits after decimal
    } else {
        ndigits -= cvt->scale;  // adjust number of digits (see fortran spec)

⌨️ 快捷键说明

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