]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - libdecnumber/decPacked.c
Imported gcc-4.4.3
[msp430-gcc.git] / libdecnumber / decPacked.c
diff --git a/libdecnumber/decPacked.c b/libdecnumber/decPacked.c
new file mode 100644 (file)
index 0000000..5f0d36c
--- /dev/null
@@ -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 */
+