view libdecnumber/decPacked.c @ 0:a06113de4d67

first commit
author kent <kent@cr.ie.u-ryukyu.ac.jp>
date Fri, 17 Jul 2009 14:47:48 +0900
parents
children 77e2b8dfacca
line wrap: on
line source

/* 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 */