Logo Search packages:      
Sourcecode: libdfp version File versions  Download package

decPacked.c

/* Packed decimal conversion module for the decNumber C Library.
   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
   Contributed by IBM Corporation.  Author Mike Cowlishaw.

   This file is part of GCC.

   GCC is free software; you can redistribute it and/or modify it under
   the terms of the GNU General Public License as published by the Free
   Software Foundation; either version 3, or (at your option) any later
   version.

   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   for more details.

Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.

You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
<http://www.gnu.org/licenses/>.  */

/* ------------------------------------------------------------------ */
/* Packed Decimal conversion module                         */
/* ------------------------------------------------------------------ */
/* This module comprises the routines for Packed Decimal format         */
/* numbers.  Conversions are supplied to and from decNumber, which in */
/* turn supports:                                     */
/*   conversions to and from string                         */
/*   arithmetic routines                                    */
/*   utilities.                                             */
/* Conversions from decNumber to and from densely packed decimal      */
/* formats are provided by the decimal32 through decimal128 modules.  */
/* ------------------------------------------------------------------ */

#include <string.h>           /* for NULL */
#include "decNumber.h"        /* base number library */
#include "decPacked.h"        /* packed decimal */
#include "decNumberLocal.h"   /* decNumber local types, etc. */

/* ------------------------------------------------------------------ */
/* decPackedFromNumber -- convert decNumber to BCD Packed Decimal     */
/*                                                    */
/*   bcd    is the BCD bytes                                */
/*   length is the length of the BCD array                        */
/*   scale  is the scale result                                   */
/*   dn         is the decNumber                                  */
/*   returns bcd, or NULL if error                          */
/*                                                    */
/* The number is converted to a BCD packed decimal byte array,          */
/* right aligned in the bcd array, whose length is indicated by the   */
/* second parameter.  The final 4-bit nibble in the array will be a   */
/* sign nibble, C (1100) for + and D (1101) for -.  Unused bytes and  */
/* nibbles to the left of the number are set to 0.                */
/*                                                    */
/* scale is set to the scale of the number (this is the exponent,     */
/* negated).  To force the number to a specified scale, first use the */
/* decNumberRescale routine, which will round and change the exponent */
/* as necessary.                                      */
/*                                                    */
/* If there is an error (that is, the decNumber has too many digits   */
/* to fit in length bytes, or it is a NaN or Infinity), NULL is         */
/* returned and the bcd and scale results are unchanged.  Otherwise   */
/* bcd is returned.                                         */
/* ------------------------------------------------------------------ */
uByte * decPackedFromNumber(uByte *bcd, Int length, Int *scale,
                      const decNumber *dn) {
  const Unit *up=dn->lsu;     /* Unit array pointer */
  uByte obyte, *out;          /* current output byte, and where it goes */
  Int indigs=dn->digits;      /* digits processed */
  uInt cut=DECDPUN;           /* downcounter per Unit */
  uInt u=*up;                 /* work */
  uInt nib;             /* .. */
  #if DECDPUN<=4
  uInt temp;                  /* .. */
  #endif

  if (dn->digits>length*2-1              /* too long .. */
   ||(dn->bits & DECSPECIAL)) return NULL;   /* .. or special -- hopeless */

  if (dn->bits&DECNEG) obyte=DECPMINUS;        /* set the sign .. */
   else                  obyte=DECPPLUS;
  *scale=-dn->exponent;                  /* .. and scale */

  /* loop from lowest (rightmost) byte */
  out=bcd+length-1;                      /* -> final byte */
  for (; out>=bcd; out--) {
    if (indigs>0) {
      if (cut==0) {
      up++;
      u=*up;
      cut=DECDPUN;
      }
      #if DECDPUN<=4
      temp=(u*6554)>>16;         /* fast /10 */
      nib=u-X10(temp);
      u=temp;
      #else
      nib=u%10;            /* cannot use *6554 trick :-( */
      u=u/10;
      #endif
      obyte|=(nib<<4);
      indigs--;
      cut--;
      }
    *out=obyte;
    obyte=0;                     /* assume 0 */
    if (indigs>0) {
      if (cut==0) {
      up++;
      u=*up;
      cut=DECDPUN;
      }
      #if DECDPUN<=4
      temp=(u*6554)>>16;         /* as above */
      obyte=(uByte)(u-X10(temp));
      u=temp;
      #else
      obyte=(uByte)(u%10);
      u=u/10;
      #endif
      indigs--;
      cut--;
      }
    } /* loop */

  return bcd;
  } /* decPackedFromNumber */

/* ------------------------------------------------------------------ */
/* decPackedToNumber -- convert BCD Packed Decimal to a decNumber     */
/*                                                    */
/*   bcd    is the BCD bytes                                */
/*   length is the length of the BCD array                        */
/*   scale  is the scale associated with the BCD integer          */
/*   dn         is the decNumber [with space for length*2 digits]       */
/*   returns dn, or NULL if error                           */
/*                                                    */
/* The BCD packed decimal byte array, together with an associated     */
/* scale, is converted to a decNumber.    The BCD array is assumed full */
/* of digits, and must be ended by a 4-bit sign nibble in the least   */
/* significant four bits of the final byte.                       */
/*                                                    */
/* The scale is used (negated) as the exponent of the decNumber.      */
/* Note that zeros may have a sign and/or a scale.                */
/*                                                    */
/* The decNumber structure is assumed to have sufficient space to     */
/* hold the converted number (that is, up to length*2-1 digits), so   */
/* no error is possible unless the adjusted exponent is out of range, */
/* no sign nibble was found, or a sign nibble was found before the    */
/* final nibble.  In these error cases, NULL is returned and the      */
/* decNumber will be 0.                                     */
/* ------------------------------------------------------------------ */
decNumber * decPackedToNumber(const uByte *bcd, Int length,
                        const Int *scale, decNumber *dn) {
  const uByte *last=bcd+length-1;  /* -> last byte */
  const uByte *first;            /* -> first non-zero byte */
  uInt      nib;                 /* work nibble */
  Unit      *up=dn->lsu;               /* output pointer */
  Int digits;                    /* digits count */
  Int cut=0;                     /* phase of output */

  decNumberZero(dn);             /* default result */
  last=&bcd[length-1];
  nib=*last & 0x0f;              /* get the sign */
  if (nib==DECPMINUS || nib==DECPMINUSALT) dn->bits=DECNEG;
   else if (nib<=9) return NULL;   /* not a sign nibble */

  /* skip leading zero bytes [final byte is always non-zero, due to sign] */
  for (first=bcd; *first==0;) first++;
  digits=(last-first)*2+1;          /* calculate digits .. */
  if ((*first & 0xf0)==0) digits--; /* adjust for leading zero nibble */
  if (digits!=0) dn->digits=digits; /* count of actual digits [if 0, */
                              /* leave as 1] */

  /* check the adjusted exponent; note that scale could be unbounded */
  dn->exponent=-*scale;             /* set the exponent */
  if (*scale>=0) {                  /* usual case */
    if ((dn->digits-*scale-1)<-DECNUMMAXE) {      /* underflow */
      decNumberZero(dn);
      return NULL;}
    }
   else { /* -ve scale; +ve exponent */
    /* need to be careful to avoid wrap, here, also BADINT case */
    if ((*scale<-DECNUMMAXE)        /* overflow even without digits */
       || ((dn->digits-*scale-1)>DECNUMMAXE)) { /* overflow */
      decNumberZero(dn);
      return NULL;}
    }
  if (digits==0) return dn;         /* result was zero */

  /* copy the digits to the number's units, starting at the lsu */
  /* [unrolled] */
  for (;;) {                        /* forever */
    /* left nibble first */
    nib=(unsigned)(*last & 0xf0)>>4;
    /* got a digit, in nib */
    if (nib>9) {decNumberZero(dn); return NULL;}

    if (cut==0) *up=(Unit)nib;
     else *up=(Unit)(*up+nib*DECPOWERS[cut]);
    digits--;
    if (digits==0) break;           /* got them all */
    cut++;
    if (cut==DECDPUN) {
      up++;
      cut=0;
      }
    last--;                   /* ready for next */
    nib=*last & 0x0f;               /* get right nibble */
    if (nib>9) {decNumberZero(dn); return NULL;}

    /* got a digit, in nib */
    if (cut==0) *up=(Unit)nib;
     else *up=(Unit)(*up+nib*DECPOWERS[cut]);
    digits--;
    if (digits==0) break;           /* got them all */
    cut++;
    if (cut==DECDPUN) {
      up++;
      cut=0;
      }
    } /* forever */

  return dn;
  } /* decPackedToNumber */


Generated by  Doxygen 1.6.0   Back to index