/**************************************************************************** * * 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 from string to tbyte format (long double) * it doesn't depend on processor/math coprocessor type * ****************************************************************************/ /* It use 96bit x 96bit -> 192bit multiply for converting decimal to 80-bit extended double. Math operation is on 96-bit wide operands (mantisa). */ #include #include "globals.h" #include "tbyte.h" #if defined(LLONG_MAX) || defined(__GNUC__) || defined(__TINYC__) #define MAXUI64BIT 0x8000000000000000ULL #define MAXUI64 0xffffffffffffffffULL #else #define MAXUI64BIT 0x8000000000000000ui64 #define MAXUI64 0xffffffffffffffffui64 #endif union u192 { uint_64 m64[3]; uint_32 m32[6]; }; union u96 { uint_32 m32[3]; }; /* extended (112-bit, 96+16) long double */ struct ELD { uint_32 m32[3]; unsigned short e; }; #define EXPONENT_BIAS 0x3FFF #define SET_SIGN(x,y) if( y < 0 ) x->e |= 0x8000 #define U96ISNOTZERO(x) (x.m32[0] || x.m32[1] || x.m32[2]) #define MAX_EXP_INDEX 13 /* # of items in tab_plus_exp[] and tab_minus_exp[] */ static const struct ELD tab_plus_exp[MAX_EXP_INDEX] = { { { 0x00000000UL, 0x00000000UL, 0xA0000000UL }, 0x4002 }, /* 1e1L */ { { 0x00000000UL, 0x00000000UL, 0xC8000000UL }, 0x4005 }, /* 1e2L */ { { 0x00000000UL, 0x00000000UL, 0x9C400000UL }, 0x400C }, /* 1e4L */ { { 0x00000000UL, 0x00000000UL, 0xBEBC2000UL }, 0x4019 }, /* 1e8L */ { { 0x00000000UL, 0x04000000UL, 0x8E1BC9BFUL }, 0x4034 }, /* 1e16L */ { { 0xF0200000UL, 0x2B70B59DUL, 0x9DC5ADA8UL }, 0x4069 }, /* 1e32L */ { { 0x3CBF6B71UL, 0xFFCFA6D5UL, 0xC2781F49UL }, 0x40D3 }, /* 1e64L */ { { 0xC66F336BUL, 0x80E98CDFUL, 0x93BA47C9UL }, 0x41A8 }, /* 1e128L */ { { 0xDDBB9018UL, 0x9DF9DE8DUL, 0xAA7EEBFBUL }, 0x4351 }, /* 1e256L */ { { 0xCC655C4BUL, 0xA60E91C6UL, 0xE319A0AEUL }, 0x46A3 }, /* 1e512L */ { { 0x650D3D17UL, 0x81750C17UL, 0xC9767586UL }, 0x4D48 }, /* 1e1024L*/ { { 0xA74D28B1UL, 0xC53D5DE4UL, 0x9E8b3B5DUL }, 0x5A92 }, /* 1e2048L*/ { { 0xC94C14F7UL, 0x8A20979AUL, 0xC4605202UL }, 0x7525 } /* 1e4096L*/ }; static const struct ELD tab_minus_exp[MAX_EXP_INDEX] = { { { 0xCCCCCCCDUL, 0xCCCCCCCCUL, 0xCCCCCCCCUL }, 0x3FFB }, /* 1e-1L */ { { 0x3D70A3D7UL, 0x70A3D70AUL, 0xA3D70A3DUL }, 0x3FF8 }, /* 1e-2L */ { { 0xD3C36113UL, 0xE219652BUL, 0xD1B71758UL }, 0x3FF1 }, /* 1e-4L */ { { 0xFDC20D2AUL, 0x8461CEFCUL, 0xABCC7711UL }, 0x3FE4 }, /* 1e-8L */ { { 0x4C2EBE65UL, 0xC44DE15BUL, 0xE69594BEUL }, 0x3FC9 }, /* 1e-16L */ { { 0x67DE18E7UL, 0x453994BAUL, 0xCFB11EADUL }, 0x3F94 }, /* 1e-32L */ { { 0x3F2398CCUL, 0xA539E9A5UL, 0xA87FEA27UL }, 0x3F2A }, /* 1e-64L */ { { 0xAC7CB3D9UL, 0x64BCE4A0UL, 0xDDD0467CUL }, 0x3E55 }, /* 1e-128L */ { { 0xFA911122UL, 0x637A1939UL, 0xC0314325UL }, 0x3CAC }, /* 1e-256L */ { { 0x7132D2E4UL, 0xDB23D21CUL, 0x9049EE32UL }, 0x395A }, /* 1e-512L */ { { 0x87A600A6UL, 0xDA57C0BDUL, 0xA2A682A5UL }, 0x32B5 }, /* 1e-1024L*/ { { 0x4925110FUL, 0x34362DE4UL, 0xCEAE534FUL }, 0x256B }, /* 1e-2048L*/ { { 0x2DE37E46UL, 0xD2CE9FDEUL, 0xA6DD04C8UL }, 0x0AD8 } /* 1e-4096L*/ }; static int cmp_u96_max( const union u96 *x ) /******************************************* compare u96 with maximum value before u96 overflow after multiply by 10 */ { if( x->m32[2] > 0x19999999UL ) { return( 1 ); } else if( x->m32[2] < 0x19999999UL ) { return( -1 ); } else if( x->m32[1] > 0x99999999UL ) { return( 1 ); } else if( x->m32[1] < 0x99999999UL ) { return( -1 ); } else if( x->m32[0] > 0x99999998UL ) { return( 1 ); } else if( x->m32[0] < 0x99999998UL ) { return( -1 ); } else { return( 0 ); } } static int add_check_u96_overflow( union u96 *x, unsigned int c ) /**************************************************************** test u96 overflow after multiply by 10 add one decimal digit to u96 */ { uint_64 cy; int i; if( cmp_u96_max( x ) > 0 ) { return( 1 ); } else { cy = c; for( i = 0; i < 3; i++ ) { cy += (uint_64)x->m32[i] * 10; x->m32[i] = cy; cy >>= 32; } return( 0 ); } } static int bitsize32( uint_32 x ) /******************************** calculate bitsize for uint_32 */ { int i; for( i = 32; i > 0 ; i-- ) { if( x & 0x80000000U ) break; x <<= 1; } return( i ); } static int bitsize64( uint_64 x ) /******************************** calculate bitsize for uint_64 */ { int i; for( i = 64; i > 0 ; i-- ) { if( x & MAXUI64BIT ) break; x <<= 1; } return( i ); } static int U96LD( const union u96 *op, struct ELD *res ) /******************************************************* convert u96 into internal extended long double */ { int bs; int shft; memcpy( res, op, sizeof( union u96 ) ); bs = bitsize32( res->m32[2] ) + 64; if( bs == 64 ) { res->m32[2] = res->m32[1]; res->m32[1] = res->m32[0]; res->m32[0] = 0; bs = bitsize32( res->m32[2] ) + 32; } if( bs == 32 ) { res->m32[2] = res->m32[1]; res->m32[1] = res->m32[0]; res->m32[0] = 0; bs = bitsize32( res->m32[2] ); } if( bs == 0 ) { res->e = 0; } else { res->e = bs - 1 + EXPONENT_BIAS; bs %= 32; if( bs ) { shft = 32 - bs; res->m32[2] <<= shft; res->m32[2] |= res->m32[1] >> bs; res->m32[1] <<= shft; res->m32[1] |= res->m32[0] >> bs; res->m32[0] <<= shft; } } return( 0 ); } static int normalize( union u192 *res ) /************************************** normalize internal extended long double u192 return exponent shift */ { int shft; int bs; int bs1; bs = bitsize64( res->m64[2] ) + 128; if( bs == 128 ) { res->m64[2] = res->m64[1]; res->m64[1] = res->m64[0]; res->m64[0] = 0; bs = bitsize64( res->m64[2] ) + 64; } if( bs == 64 ) { res->m64[2] = res->m64[1]; res->m64[1] = res->m64[0]; res->m64[0] = 0; bs = bitsize64( res->m64[2] ); } if( bs == 0 ) { return( 0 ); } bs1 = bs % 64; if ( bs1 ) { shft = 64 - bs1; res->m64[2] <<= shft; res->m64[2] |= res->m64[1] >> bs1; res->m64[1] <<= shft; res->m64[1] |= res->m64[0] >> bs1; res->m64[0] <<= shft; } return( bs - 192 ); } static int add192( union u192 *res, const uint_64 x, int pos ) /************************************************************* add uint_64 to u192 on uint_32 position */ { uint_64 cy; int i; cy = (uint_32)x; for( i = pos; i < 6; i++ ) { cy += res->m32[i]; res->m32[i] = cy; cy >>= 32; } cy = x >> 32; for( i = pos + 1; i < 6; i++ ) { cy += res->m32[i]; res->m32[i] = cy; cy >>= 32; } return( 0 ); } static int multiply( const struct ELD *op1, const struct ELD *op2, struct ELD *res ) /*********************************************************************************** multiply u96 by u96 into u96 normalize and round result */ { uint_64 x1; union u192 r1; int_32 exp; exp = (int_32)(op1->e & 0x7fff) + (int_32)(op2->e & 0x7fff) - EXPONENT_BIAS + 1; r1.m64[0] = (uint_64)(op1->m32[0]) * (uint_64)(op2->m32[0]); r1.m64[1] = (uint_64)(op1->m32[1]) * (uint_64)(op2->m32[1]); r1.m64[2] = (uint_64)(op1->m32[2]) * (uint_64)(op2->m32[2]); x1 = (uint_64)(op1->m32[1]) * (uint_64)(op2->m32[0]); add192( &r1, x1, 1 ); x1 = (uint_64)(op1->m32[0]) * (uint_64)(op2->m32[1]); add192( &r1, x1, 1 ); x1 = (uint_64)(op1->m32[0]) * (uint_64)(op2->m32[2]); add192( &r1, x1, 2 ); x1 = (uint_64)(op1->m32[2]) * (uint_64)(op2->m32[0]); add192( &r1, x1, 2 ); x1 = (uint_64)(op1->m32[1]) * (uint_64)(op2->m32[2]); add192( &r1, x1, 3 ); x1 = (uint_64)(op1->m32[2]) * (uint_64)(op2->m32[1]); add192( &r1, x1, 3 ); exp += normalize( &r1 ); /* round result */ if( r1.m32[2] & 0x80000000U ) { if( r1.m32[5] == 0xffffffffU && r1.m32[4] == 0xffffffffU && r1.m32[3] == 0xffffffffU ) { r1.m32[3] = 0; r1.m32[4] = 0; r1.m32[5] = 0x80000000U; exp++; } else { x1 = 1L; add192( &r1, x1, 3 ); } } res->m32[0] = r1.m32[3]; res->m32[1] = r1.m32[4]; res->m32[2] = r1.m32[5]; res->e = exp; return( 0 ); } static int TB_create( union u96 *value, int_32 exponent, struct TB_LD *ld ) /************************************************************************** create tbyte/long double from u96 value and decimal exponent, round result */ { const struct ELD *tabExp; int i; struct ELD res; if( exponent < 0 ) { exponent = -exponent; tabExp = tab_minus_exp; } else { tabExp = tab_plus_exp; } U96LD(value, &res); for( i = 0; i < MAX_EXP_INDEX; i++ ) { if ( exponent & 1 ) { multiply(&res, tabExp + i, &res); } exponent >>= 1; if( exponent == 0 ) break; } if( exponent != 0 ) { /* exponent overflow */ } ld->e = res.e; ld->m = res.m32[1] + ((uint_64)res.m32[2] << 32) ; /* round result */ if( res.m32[0] & 0x80000000U ) { if( ld->m == MAXUI64 ) { ld->m = MAXUI64BIT; ld->e++; } else { ld->m++; } } return( 0 ); } struct TB_LD *strtotb( const char *p, struct TB_LD *ld, char negative ) /********************************************************************** convert string into tbyte/long double set result sign */ { int sign = +1; int exp_sign = +1; int_32 exp_value; int overflow; int_32 exp1; int_32 exponent; int_32 exponent_tmp; union u96 value; union u96 value_tmp; while ( isspace( *p ) ) p++; switch (*p) { case '-': sign = -1; case '+': p++; default : break; } if( negative ) { sign = -sign; } memset( &value, 0, sizeof( value ) ); memset( &value_tmp, 0, sizeof( value_tmp ) ); //exponent = 0; exp1 = 0; exponent_tmp = 0; overflow = 0; while ( (unsigned int)(*p - '0') < 10u ) { if( overflow ) { exponent_tmp++; exp1++; } else { if( add_check_u96_overflow( &value_tmp, *p - '0' ) ) { overflow = 1; exponent_tmp++; exp1++; } else if( *p != '0' ) { memcpy( &value, &value_tmp, sizeof( value ) ); exp1 = 0; } else if( U96ISNOTZERO(value) ) { exp1++; } } p++; } exponent = exp1; if ( *p == '.' ) { p++; while ( (unsigned int)(*p - '0') < 10u ) { if( overflow == 0 ) { if( add_check_u96_overflow( &value_tmp, *p - '0' ) ) { overflow = 1; } else { exponent_tmp--; if( *p != '0' ) { memcpy( &value, &value_tmp, sizeof( value ) ); exponent = exponent_tmp; } } } p++; } } exp_value = 0; if ( (*p | 0x20) == 'e' ) { switch ( *++p ) { case '-': exp_sign = -1; case '+': p++; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': break; default : ld->m = 0; ld->e = 0; SET_SIGN( ld, sign ); return( ld ); } while ( (unsigned int)(*p - '0') < 10u ) exp_value = 10 * exp_value + (*p++ - '0'); if( exp_sign < 0 ) exp_value = -exp_value; } exp_value += exponent; TB_create( &value, exp_value, ld ); SET_SIGN( ld, sign ); return( ld ); }