diff 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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdecnumber/decPacked.c	Fri Jul 17 14:47:48 2009 +0900
@@ -0,0 +1,230 @@
+/* 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 */
+