/***********************************************************************/ /* */ /* Objective Caml */ /* */ /* Xavier Leroy, projet Cristal, INRIA Rocquencourt */ /* */ /* Copyright 2003 Institut National de Recherche en Informatique et */ /* en Automatique. All rights reserved. This file is distributed */ /* under the terms of the GNU Library General Public License, with */ /* the special exception on linking described in file ../../LICENSE. */ /* */ /***********************************************************************/ /* $Id$ */ /**** Generic operations on digits ****/ /* These macros can be defined in the machine-specific include file. Below are the default definitions (in plain C). Except for BngMult, all macros are guaranteed to evaluate their arguments exactly once. */ #ifndef BngAdd2 /* res = arg1 + arg2. carryout = carry out. */ #define BngAdd2(res,carryout,arg1,arg2) { \ bngdigit tmp1, tmp2; \ tmp1 = arg1; \ tmp2 = tmp1 + (arg2); \ carryout = (tmp2 < tmp1); \ res = tmp2; \ } #endif #ifndef BngAdd2Carry /* res = arg1 + arg2 + carryin. carryout = carry out. */ #define BngAdd2Carry(res,carryout,arg1,arg2,carryin) { \ bngdigit tmp1, tmp2, tmp3; \ tmp1 = arg1; \ tmp2 = tmp1 + (arg2); \ tmp3 = tmp2 + (carryin); \ carryout = (tmp2 < tmp1) + (tmp3 < tmp2); \ res = tmp3; \ } #endif #ifndef BngAdd3 /* res = arg1 + arg2 + arg3. Each carry increments carryaccu. */ #define BngAdd3(res,carryaccu,arg1,arg2,arg3) { \ bngdigit tmp1, tmp2, tmp3; \ tmp1 = arg1; \ tmp2 = tmp1 + (arg2); \ carryaccu += (tmp2 < tmp1); \ tmp3 = tmp2 + (arg3); \ carryaccu += (tmp3 < tmp2); \ res = tmp3; \ } #endif #ifndef BngSub2 /* res = arg1 - arg2. carryout = carry out. */ #define BngSub2(res,carryout,arg1,arg2) { \ bngdigit tmp1, tmp2; \ tmp1 = arg1; \ tmp2 = arg2; \ res = tmp1 - tmp2; \ carryout = (tmp1 < tmp2); \ } #endif #ifndef BngSub2Carry /* res = arg1 - arg2 - carryin. carryout = carry out. */ #define BngSub2Carry(res,carryout,arg1,arg2,carryin) { \ bngdigit tmp1, tmp2, tmp3; \ tmp1 = arg1; \ tmp2 = arg2; \ tmp3 = tmp1 - tmp2; \ res = tmp3 - (carryin); \ carryout = (tmp1 < tmp2) + (tmp3 < carryin); \ } #endif #ifndef BngSub3 /* res = arg1 - arg2 - arg3. Each carry increments carryaccu. */ #define BngSub3(res,carryaccu,arg1,arg2,arg3) { \ bngdigit tmp1, tmp2, tmp3, tmp4; \ tmp1 = arg1; \ tmp2 = arg2; \ tmp3 = arg3; \ tmp4 = tmp1 - tmp2; \ res = tmp4 - tmp3; \ carryaccu += (tmp1 < tmp2) + (tmp4 < tmp3); \ } #endif #define BngLowHalf(d) ((d) & (((bngdigit)1 << BNG_BITS_PER_HALF_DIGIT) - 1)) #define BngHighHalf(d) ((d) >> BNG_BITS_PER_HALF_DIGIT) #ifndef BngMult /* resl = low digit of product arg1 * arg2 resh = high digit of product arg1 * arg2. */ #define BngMult(resh,resl,arg1,arg2) { \ bngdigit p11 = BngLowHalf(arg1) * BngLowHalf(arg2); \ bngdigit p12 = BngLowHalf(arg1) * BngHighHalf(arg2); \ bngdigit p21 = BngHighHalf(arg1) * BngLowHalf(arg2); \ bngdigit p22 = BngHighHalf(arg1) * BngHighHalf(arg2); \ resh = p22 + (p12 >> BNG_BITS_PER_HALF_DIGIT) \ + (p21 >> BNG_BITS_PER_HALF_DIGIT); \ BngAdd3(resl, resh, \ p11, p12 << BNG_BITS_PER_HALF_DIGIT, p21 << BNG_BITS_PER_HALF_DIGIT); \ } #endif #ifndef BngDiv /* Divide the double-width number nh:nl by d. Require d != 0 and nh < d. Store quotient in quo, remainder in rem. Can be slow if d is not normalized. */ #define BngDiv(quo,rem,nh,nl,d) bng_div_aux(&(quo),&(rem),nh,nl,d) #define BngDivNeedsNormalization static void bng_div_aux(bngdigit * quo, bngdigit * rem, bngdigit nh, bngdigit nl, bngdigit d) { bngdigit dl, dh, ql, qh, pl, ph, nsaved; dl = BngLowHalf(d); dh = BngHighHalf(d); /* Under-estimate the top half of the quotient (qh) */ qh = nh / (dh + 1); /* Shift nh:nl right by BNG_BITS_PER_HALF_DIGIT bits, so that we focus on the top 1.5 digits of the numerator. Then, subtract (qh * d) from nh:nl. */ nsaved = BngLowHalf(nl); ph = qh * dh; pl = qh * dl; nh -= ph; /* Subtract before shifting so that carry propagates for free */ nl = (nl >> BNG_BITS_PER_HALF_DIGIT) | (nh << BNG_BITS_PER_HALF_DIGIT); nh = (nh >> BNG_BITS_PER_HALF_DIGIT); nh -= (nl < pl); /* Borrow */ nl -= pl; /* Adjust estimate qh until nh:nl < 0:d */ while (nh != 0 || nl >= d) { nh -= (nl < d); /* Borrow */ nl -= d; qh++; } /* Under-estimate the bottom half of the quotient (ql) */ ql = nl / (dh + 1); /* Shift nh:nl left by BNG_BITS_PER_HALF_DIGIT bits, restoring the low bits we saved earlier, so that we focus on the bottom 1.5 digit of the numerator. Then, subtract (ql * d) from nh:nl. */ ph = ql * dh; pl = ql * dl; nl -= ph; /* Subtract before shifting so that carry propagates for free */ nh = (nl >> BNG_BITS_PER_HALF_DIGIT); nl = (nl << BNG_BITS_PER_HALF_DIGIT) | nsaved; nh -= (nl < pl); /* Borrow */ nl -= pl; /* Adjust estimate ql until nh:nl < 0:d */ while (nh != 0 || nl >= d) { nh -= (nl < d); /* Borrow */ nl -= d; ql++; } /* We're done */ *quo = (qh << BNG_BITS_PER_HALF_DIGIT) | ql; *rem = nl; } #endif