tbyte.c

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

C
463
字号
/****************************************************************************
*
*                            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 <string.h>
#include <ctype.h>
#include "tbyte.h"
#include "watcom.h"

typedef unsigned __int64 u64;
typedef unsigned_32 u32;

typedef union {
    u64 m64[3];
    u32 m32[6];
} u192;

typedef union {
    u32 m32[3];
} u96;

typedef struct {
    u32 m32[3];
    unsigned short e;
} ELD;

#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

static 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 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( 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( u96 *x, unsigned int c)
/**********************************************
    test u96 overflow after multiply by 10
    add one decimal digit to u96
*/
{
    u64 cy;
    int i;

    if( cmp_u96_max(x) > 0 ) {
        return 1;
    } else {
        cy = c;
        for( i = 0; i < 3; i++ ) {
            cy += (u64)x->m32[i] * 10;
            x->m32[i] = cy;
            cy >>= 32;
        }
        return 0;
    }
}

static int bitsize32(u32 x)
/**********************************************
    calculate bitsize for u32
*/
{
    int i;

    for( i = 32; i > 0 ; i-- ) {
        if( x & 0x80000000U ) break;
        x <<= 1;
    }
    return i;
}

static int bitsize64(u64 x)
/**********************************************
    calculate bitsize for u64
*/
{
    int i;

    for( i = 64; i > 0 ; i-- ) {
        if( x & 0x8000000000000000ULL ) break;
        x <<= 1;
    }
    return i;
}

static int U96LD(u96 *op, ELD *res)
/**************************************************
    convert u96 into internal extended long double
*/
{
    int bs;
    int shft;

    memcpy(res, op, sizeof(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(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(u192 *res, u64 x, int pos)
/**************************************************
    add u64 to u192 on u32 position
*/
{
    u64 cy;
    int i;

    cy = (u32)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(ELD *op1, ELD *op2, ELD *res)
/**************************************************
    multiply u96 by u96 into u96
    normalize and round result
*/
{
    u64 x1;
    u192 r1;
    long exp;

    exp = (long)(op1->e & 0x7fff) + (long)(op2->e & 0x7fff) - EXPONENT_BIAS + 1;
    r1.m64[0] = (u64)(op1->m32[0]) * (u64)(op2->m32[0]);
    r1.m64[1] = (u64)(op1->m32[1]) * (u64)(op2->m32[1]);
    r1.m64[2] = (u64)(op1->m32[2]) * (u64)(op2->m32[2]);
    x1 = (u64)(op1->m32[1]) * (u64)(op2->m32[0]);
    add192(&r1, x1, 1);
    x1 = (u64)(op1->m32[0]) * (u64)(op2->m32[1]);
    add192(&r1, x1, 1);
    x1 = (u64)(op1->m32[0]) * (u64)(op2->m32[2]);
    add192(&r1, x1, 2);
    x1 = (u64)(op1->m32[2]) * (u64)(op2->m32[0]);
    add192(&r1, x1, 2);
    x1 = (u64)(op1->m32[1]) * (u64)(op2->m32[2]);
    add192(&r1, x1, 3);
    x1 = (u64)(op1->m32[2]) * (u64)(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(u96 *value, long exponent, TB_LD *ld)
/**************************************************
    create tbyte/long double from u96 value and
    decimal exponent, round result
*/
{
    ELD *tabExp;
    int i;
    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] + ((u64)res.m32[2] << 32) ;
    // round result
    if(res.m32[0] & 0x80000000U) {
        if( ld->m == 0xffffffffffffffffULL ) {
            ld->m = 0x8000000000000000ULL;
            ld->e++;
        } else {
            ld->m++;
        }
    }
    return 0;
}

TB_LD * strtotb(char *p, TB_LD * ld, char negative)
/**************************************************
    convert string into tbyte/long double
    set result sign
*/
{
    int              sign = +1;
    int              exp_sign = +1;
    long             exp_value;
    int              overflow;
    long             exp1;
    long             exponent;
    long             exponent_tmp;
    u96              value;
    u96              value_tmp;

    while ( isspace(*p) ) p++;
    switch (*p) {
    case '-':
        sign = -1;
    case '+':
        p++;
    default :
        break;
    }
    if( negative ) {
        sign = (sign > 0) ? -1 : +1;
    }
    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;
}

⌨️ 快捷键说明

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