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 + -
显示快捷键?