| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| /* |
| * $Id$ |
| * |
| * Revision History |
| * =================== |
| * $Log: bcd2.c,v $ |
| * Revision 1.1 2007/10/24 20:25:23 cktan |
| * new |
| * |
| * Revision 1.2 2007/04/07 08:10:40 cmcdevitt |
| * Fixes for dbgen with large scale factors |
| * |
| * Revision 1.2 2005/01/03 20:08:58 jms |
| * change line terminations |
| * |
| * Revision 1.1.1.1 2004/11/24 23:31:45 jms |
| * re-establish external server |
| * |
| * Revision 1.1.1.1 2003/04/03 18:54:21 jms |
| * recreation after CVS crash |
| * |
| * Revision 1.1.1.1 2003/04/03 18:54:21 jms |
| * initial checkin |
| * |
| * |
| */ |
| /* |
| * bcd.c: conversion routines for multi-byte arithmetic |
| * |
| * defined routines: |
| * bin_bcd2(long binary, long *low_res, long *high_res) |
| * bcd2_bin(long *dest, long bcd) |
| * bcd2_add(long *bcd_low, long *bcd_high, long addend) |
| * bcd2_sub(long *bcd_low, long *bcd_high, long subend) |
| * bcd2_mul(long *bcd_low, long *bcd_high, long multiplier) |
| * bcd2_div(long *bcd_low, long *bcd_high, long divisor) |
| * long bcd2_mod(long *bcd_low, long *bcd_high, long modulo) |
| * long bcd2_cmp(long *bcd_low, long *bcd_high, long compare) |
| */ |
| #include <stdio.h> |
| #include "bcd2.h" /* for function prototypes */ |
| |
| #define DIGITS_PER_LONG 7 |
| #define WORD_DIVISOR 10000000 |
| #define GET_DIGIT(num, low, high) \ |
| ((num) >= DIGITS_PER_LONG)? \ |
| (high & (0xF << (4 * ((num) - DIGITS_PER_LONG)))) \ |
| >> (((num) - DIGITS_PER_LONG) * 4): \ |
| (low & (0xF << (4 * (num)))) >> ((num) * 4) |
| #define SET_DIGIT(value, num, low, high) \ |
| if ((num) >= DIGITS_PER_LONG) \ |
| { \ |
| *high &= \ |
| (0xFFFFFFF ^ (0xF << (4 * ((num) - DIGITS_PER_LONG)))); \ |
| *high |= (value << (4 * ((num) - DIGITS_PER_LONG))); \ |
| } \ |
| else \ |
| { \ |
| *low = (*low & (0xFFFFFFF ^ (0xF << (4 * (num))))); \ |
| *low |= (value << (4 * (num))); \ |
| } |
| int |
| bin_bcd2(long binary, long *low_res, long *high_res) |
| { |
| char number[15], |
| *current; |
| int count; |
| long *dest; |
| |
| *low_res = *high_res = 0; |
| sprintf(number, "%014ld", binary); |
| for (current = number, count=13; *current; current++, count--) |
| { |
| dest = (count < DIGITS_PER_LONG)?low_res:high_res; |
| *dest = *dest << 4; |
| *dest |= *current - '0'; |
| } |
| return(0); |
| } |
| |
| int |
| bcd2_bin(long *dest, long bcd) |
| { |
| int count; |
| long mask; |
| |
| count = DIGITS_PER_LONG - 1; |
| mask = 0xF000000; |
| *dest = 0; |
| while (mask) |
| { |
| *dest *= 10; |
| *dest += (bcd & mask) >> (4 * count); |
| mask = mask >> 4; |
| count -= 1; |
| } |
| return(0); |
| } |
| |
| int |
| bcd2_add(long *bcd_low, long *bcd_high, long addend) |
| { |
| long tmp_lo, tmp_hi, carry, res; |
| int digit; |
| |
| bin_bcd2(addend, &tmp_lo, &tmp_hi); |
| carry = 0; |
| for (digit=0; digit < 14; digit++) |
| { |
| res = GET_DIGIT(digit, *bcd_low, *bcd_high); |
| res += GET_DIGIT(digit, tmp_lo, tmp_hi); |
| res += carry; |
| carry = res / 10; |
| res %= 10; |
| SET_DIGIT(res, digit, bcd_low, bcd_high); |
| } |
| return(carry); |
| } |
| |
| int |
| bcd2_sub(long *bcd_low, long *bcd_high, long subend) |
| { |
| long tmp_lo, tmp_hi, carry, res; |
| int digit; |
| |
| bin_bcd2(subend, &tmp_lo, &tmp_hi); |
| carry = 0; |
| for (digit=0; digit < 14; digit++) |
| { |
| res = GET_DIGIT(digit, *bcd_low, *bcd_high); |
| res -= GET_DIGIT(digit, tmp_lo, tmp_hi); |
| res -= carry; |
| if (res < 0) |
| { |
| res += 10; |
| carry = 1; |
| } |
| SET_DIGIT(res, digit, bcd_low, bcd_high); |
| } |
| return(carry); |
| } |
| |
| int |
| bcd2_mul(long *bcd_low, long *bcd_high, long multiplier) |
| { |
| long tmp_lo, tmp_hi, carry, m_lo, m_hi, m1, m2; |
| int udigit, ldigit, res; |
| |
| tmp_lo = *bcd_low; |
| tmp_hi = *bcd_high; |
| bin_bcd2(multiplier, &m_lo, &m_hi); |
| *bcd_low = 0; |
| *bcd_high = 0; |
| carry = 0; |
| for (ldigit=0; ldigit < 14; ldigit++) |
| { |
| m1 = GET_DIGIT(ldigit, m_lo, m_hi); |
| carry = 0; |
| for (udigit=0; udigit < 14; udigit++) |
| { |
| m2 = GET_DIGIT(udigit, tmp_lo, tmp_hi); |
| res = m1 * m2; |
| res += carry; |
| if (udigit + ldigit < 14) |
| { |
| carry = GET_DIGIT(udigit + ldigit, *bcd_low, *bcd_high); |
| res += carry; |
| } |
| carry = res / 10; |
| res %= 10; |
| if (udigit + ldigit < 14) |
| { |
| SET_DIGIT(res, udigit + ldigit, bcd_low, bcd_high); |
| } |
| } |
| } |
| return(carry); |
| } |
| |
| int |
| bcd2_div(long *bcd_low, long *bcd_high, long divisor) |
| { |
| long tmp_lo, tmp_hi, carry, d1, res, digit; |
| |
| |
| carry = 0; |
| tmp_lo = *bcd_low; |
| tmp_hi = *bcd_high; |
| *bcd_low = *bcd_high = 0; |
| for (digit=13; digit >= 0; digit--) |
| { |
| d1 = GET_DIGIT(digit, tmp_lo, tmp_hi); |
| d1 += 10 * carry; |
| res = d1 / divisor; |
| carry = d1 % divisor; |
| SET_DIGIT(res, digit, bcd_low, bcd_high); |
| } |
| return(carry); |
| } |
| |
| long |
| bcd2_mod(long *bcd_low, long *bcd_high, long modulo) |
| { |
| long tmp_low, tmp_high; |
| |
| tmp_low = *bcd_low; |
| tmp_high = *bcd_high; |
| while (tmp_high || tmp_low > modulo) |
| bcd2_sub(&tmp_low, &tmp_high, modulo); |
| return(tmp_low); |
| } |
| |
| long |
| bcd2_cmp(long *low1, long *high1, long comp) |
| { |
| long temp = 0; |
| |
| bcd2_bin(&temp, *high1); |
| if (temp > 214) |
| return(1); |
| bcd2_bin(&temp, *low1); |
| return(temp - comp); |
| } |
| |
| #ifdef TEST_BCD |
| #include <values.h> |
| |
| main() |
| { |
| long bin, low_bcd, high_bcd; |
| int i; |
| |
| bin = MAXINT; |
| printf("%ld\n", bin); |
| bin_bcd2(bin, &low_bcd, &high_bcd); |
| printf("%ld %ld\n", high_bcd, low_bcd); |
| bin = 0; |
| bcd2_bin(&bin, high_bcd); |
| bcd2_bin(&bin, low_bcd); |
| printf( "%ld\n", bin); |
| for (i=9; i >= 0; i--) |
| printf("%dth digit in %d is %d\n", |
| i, bin, GET_DIGIT(i, low_bcd, high_bcd)); |
| bcd2_add(&low_bcd, &high_bcd, MAXINT); |
| bin = 0; |
| bcd2_bin(&bin, high_bcd); |
| high_bcd = bin; |
| bin = 0; |
| bcd2_bin(&bin, low_bcd); |
| low_bcd = bin; |
| printf( "%ld%07ld\n", high_bcd, low_bcd); |
| bin_bcd2(14, &low_bcd, &high_bcd); |
| bcd2_mul(&low_bcd, &high_bcd, 23L); |
| bin = 0; |
| bcd2_bin(&bin, high_bcd); |
| bcd2_bin(&bin, low_bcd); |
| printf( "%ld\n", bin); |
| bcd2_div(&low_bcd, &high_bcd, 10L); |
| bin = 0; |
| bcd2_bin(&bin, high_bcd); |
| bcd2_bin(&bin, low_bcd); |
| printf( "%ld\n", bin); |
| } |
| #endif /* TEST */ |