// ln(r) // uses bntmp1 - bntmp6 - global temp bignumbers // SIDE-EFFECTS: // n ends up as |n| bn_t unsafe_ln_bn(bn_t r, bn_t n) { int comp, almost_match = 0; long maxval; LDBL f; bn_t orig_r, orig_n, orig_bntmp5, orig_bntmp4; int orig_bnlength, orig_padding, orig_rlength, orig_shiftfactor; // use Newton's recursive method for zeroing in on ln(n): r=r+n*exp(-r)-1 if (is_bn_neg(n) || is_bn_zero(n)) { // error, return largest neg value max_bn(r); neg_a_bn(r); return r; } f = bntofloat(n); f = logl(f); // approximate ln(x) maxval = (1L << ((intlength << 3)-1)) - 1; if (f > maxval) // check for overflow { max_bn(r); return r; } else if (f <= -maxval) { max_bn(r); neg_a_bn(r); return r; } // appears to be ok, do ln // With Newton's Method, there is no need to calculate all the digits // every time. The precision approximately doubles each iteration. // Save original values. orig_bnlength = bnlength; orig_padding = padding; orig_rlength = rlength; orig_shiftfactor = shiftfactor; orig_r = r; orig_n = n; orig_bntmp5 = bntmp5; orig_bntmp4 = bntmp4; inttobn(bntmp4, 1); // set before setting new values // calculate new starting values bnlength = intlength + (int)(LDBL_DIG/LOG10_256) + 1; // round up if (bnlength > orig_bnlength) bnlength = orig_bnlength; calc_lengths(); // adjust pointers r = orig_r + orig_bnlength - bnlength; bntmp5 = orig_bntmp5 + orig_bnlength - bnlength; bntmp4 = orig_bntmp4 + orig_bnlength - bnlength; floattobn(r, f); // start with approximate ln neg_a_bn(r); // -r copy_bn(bntmp5, r); // -r for (int i = 0; i < 25; i++) // safety net, this shouldn't ever be needed { // adjust lengths bnlength <<= 1; // double precision if (bnlength > orig_bnlength) bnlength = orig_bnlength; calc_lengths(); r = orig_r + orig_bnlength - bnlength; n = orig_n + orig_bnlength - bnlength; bntmp5 = orig_bntmp5 + orig_bnlength - bnlength; bntmp4 = orig_bntmp4 + orig_bnlength - bnlength; exp_bn(bntmp6, r); // exp(-r) unsafe_mult_bn(bntmp2, bntmp6, n); // n*exp(-r) sub_a_bn(bntmp2+shiftfactor, bntmp4); // n*exp(-r) - 1 sub_a_bn(r, bntmp2+shiftfactor); // -r - (n*exp(-r) - 1) if (bnlength == orig_bnlength && (comp = abs(cmp_bn(r, bntmp5))) < 8) // if match or almost match { if (comp < 4 // perfect or near perfect match || almost_match == 1) // close enough for 2nd time break; else // this is the first time they almost matched almost_match++; } copy_bn(bntmp5, r); // -r } // restore original values bnlength = orig_bnlength; padding = orig_padding; rlength = orig_rlength; shiftfactor = orig_shiftfactor; r = orig_r; bntmp5 = orig_bntmp5; bntmp4 = orig_bntmp4; neg_a_bn(r); // -(-r) return r; }
// sqrt(r) // uses bntmp1 - bntmp6 - global temp bignumbers // SIDE-EFFECTS: // n ends up as |n| bn_t sqrt_bn(bn_t r, bn_t n) { int comp, almost_match = 0; LDBL f; bn_t orig_r, orig_n; int orig_bnlength, orig_padding, orig_rlength, orig_shiftfactor; // use Newton's recursive method for zeroing in on sqrt(n): r=.5(r+n/r) if (is_bn_neg(n)) { // sqrt of a neg, return 0 clear_bn(r); return r; } f = bntofloat(n); if (f == 0) // division by zero will occur { clear_bn(r); // sqrt(0) = 0 return r; } f = sqrtl(f); // approximate square root // no need to check overflow // With Newton's Method, there is no need to calculate all the digits // every time. The precision approximately doubles each iteration. // Save original values. orig_bnlength = bnlength; orig_padding = padding; orig_rlength = rlength; orig_shiftfactor = shiftfactor; orig_r = r; orig_n = n; // calculate new starting values bnlength = intlength + (int)(LDBL_DIG/LOG10_256) + 1; // round up if (bnlength > orig_bnlength) bnlength = orig_bnlength; calc_lengths(); // adjust pointers r = orig_r + orig_bnlength - bnlength; floattobn(r, f); // start with approximate sqrt copy_bn(bntmp4, r); for (int i = 0; i < 25; i++) // safety net, this shouldn't ever be needed { // adjust lengths bnlength <<= 1; // double precision if (bnlength > orig_bnlength) bnlength = orig_bnlength; calc_lengths(); r = orig_r + orig_bnlength - bnlength; n = orig_n + orig_bnlength - bnlength; copy_bn(bntmp6, r); copy_bn(bntmp5, n); unsafe_div_bn(bntmp4, bntmp5, bntmp6); add_a_bn(r, bntmp4); half_a_bn(r); if (bnlength == orig_bnlength && (comp = abs(cmp_bn(r, bntmp4))) < 8) // if match or almost match { if (comp < 4 // perfect or near perfect match || almost_match == 1) // close enough for 2nd time break; else // this is the first time they almost matched almost_match++; } } // restore original values bnlength = orig_bnlength; padding = orig_padding; rlength = orig_rlength; shiftfactor = orig_shiftfactor; r = orig_r; return r; }
// atan(r) // uses bntmp1 - bntmp5 - global temp bignumbers // SIDE-EFFECTS: // n ends up as |n| or 1/|n| bn_t unsafe_atan_bn(bn_t r, bn_t n) { int comp, almost_match = 0; LDBL f; bn_t orig_r, orig_n, orig_bn_pi, orig_bntmp3; int orig_bnlength, orig_padding, orig_rlength, orig_shiftfactor; // use Newton's recursive method for zeroing in on atan(n): r=r-cos(r)(sin(r)-n*cos(r)) bool signflag = false; if (is_bn_neg(n)) { signflag = true; neg_a_bn(n); } // If n is very large, atanl() won't give enough decimal places to be a // good enough initial guess for Newton's Method. If it is larger than // say, 1, atan(n) = pi/2 - acot(n) = pi/2 - atan(1/n). f = bntofloat(n); bool large_arg = f > 1.0; if (large_arg) { unsafe_inv_bn(bntmp3, n); copy_bn(n, bntmp3); f = bntofloat(n); } clear_bn(bntmp3); // not really necessary, but makes things more consistent // With Newton's Method, there is no need to calculate all the digits // every time. The precision approximately doubles each iteration. // Save original values. orig_bnlength = bnlength; orig_padding = padding; orig_rlength = rlength; orig_shiftfactor = shiftfactor; orig_bn_pi = bn_pi; orig_r = r; orig_n = n; orig_bntmp3 = bntmp3; // calculate new starting values bnlength = intlength + (int)(LDBL_DIG/LOG10_256) + 1; // round up if (bnlength > orig_bnlength) bnlength = orig_bnlength; calc_lengths(); // adjust pointers r = orig_r + orig_bnlength - bnlength; bn_pi = orig_bn_pi + orig_bnlength - bnlength; bntmp3 = orig_bntmp3 + orig_bnlength - bnlength; f = atanl(f); // approximate arctangent // no need to check overflow floattobn(r, f); // start with approximate atan copy_bn(bntmp3, r); for (int i = 0; i < 25; i++) // safety net, this shouldn't ever be needed { // adjust lengths bnlength <<= 1; // double precision if (bnlength > orig_bnlength) bnlength = orig_bnlength; calc_lengths(); r = orig_r + orig_bnlength - bnlength; n = orig_n + orig_bnlength - bnlength; bn_pi = orig_bn_pi + orig_bnlength - bnlength; bntmp3 = orig_bntmp3 + orig_bnlength - bnlength; #ifdef CALCULATING_BIG_PI printf("\natan() loop #%i, bnlength=%i\nsincos() loops\n", i, bnlength); #endif unsafe_sincos_bn(bntmp4, bntmp5, bntmp3); // sin(r), cos(r) copy_bn(bntmp3, r); // restore bntmp3 from sincos_bn() copy_bn(bntmp1, bntmp5); unsafe_mult_bn(bntmp2, n, bntmp1); // n*cos(r) sub_a_bn(bntmp4, bntmp2+shiftfactor); // sin(r) - n*cos(r) unsafe_mult_bn(bntmp1, bntmp5, bntmp4); // cos(r) * (sin(r) - n*cos(r)) sub_a_bn(r, bntmp1+shiftfactor); // r - cos(r) * (sin(r) - n*cos(r)) #ifdef CALCULATING_BIG_PI putchar('\n'); bn_hexdump(r); #endif if (bnlength == orig_bnlength && (comp = abs(cmp_bn(r, bntmp3))) < 8) // if match or almost match { #ifdef CALCULATING_BIG_PI printf("atan() loop comp=%i\n", comp); #endif if (comp < 4 // perfect or near perfect match || almost_match == 1) // close enough for 2nd time break; else // this is the first time they almost matched almost_match++; } #ifdef CALCULATING_BIG_PI if (bnlength == orig_bnlength && comp >= 8) printf("atan() loop comp=%i\n", comp); #endif copy_bn(bntmp3, r); // make a copy for later comparison } // restore original values bnlength = orig_bnlength; padding = orig_padding; rlength = orig_rlength; shiftfactor = orig_shiftfactor; bn_pi = orig_bn_pi; r = orig_r; bntmp3 = orig_bntmp3; if (large_arg) { half_bn(bntmp3, bn_pi); // pi/2 sub_a_bn(bntmp3, r); // pi/2 - atan(1/n) copy_bn(r, bntmp3); } if (signflag) neg_a_bn(r); return r; }
// r = 1/n // uses bntmp1 - bntmp3 - global temp bignumbers // SIDE-EFFECTS: // n ends up as |n| Make copy first if necessary. bn_t unsafe_inv_bn(bn_t r, bn_t n) { long maxval; LDBL f; bn_t orig_r, orig_n; // orig_bntmp1 not needed here int orig_bnlength, orig_padding, orig_rlength, orig_shiftfactor; // use Newton's recursive method for zeroing in on 1/n : r=r(2-rn) bool signflag = false; if (is_bn_neg(n)) { // will be a lot easier to deal with just positives signflag = true; neg_a_bn(n); } f = bntofloat(n); if (f == 0) // division by zero { max_bn(r); return r; } f = 1/f; // approximate inverse maxval = (1L << ((intlength << 3)-1)) - 1; if (f > maxval) // check for overflow { max_bn(r); return r; } else if (f <= -maxval) { max_bn(r); neg_a_bn(r); return r; } // With Newton's Method, there is no need to calculate all the digits // every time. The precision approximately doubles each iteration. // Save original values. orig_bnlength = bnlength; orig_padding = padding; orig_rlength = rlength; orig_shiftfactor = shiftfactor; orig_r = r; orig_n = n; // orig_bntmp1 = bntmp1; // calculate new starting values bnlength = intlength + (int)(LDBL_DIG/LOG10_256) + 1; // round up if (bnlength > orig_bnlength) bnlength = orig_bnlength; calc_lengths(); // adjust pointers r = orig_r + orig_bnlength - bnlength; // bntmp1 = orig_bntmp1 + orig_bnlength - bnlength; floattobn(r, f); // start with approximate inverse clear_bn(bntmp2); // will be used as 1.0 and 2.0 for (int i = 0; i < 25; i++) // safety net, this shouldn't ever be needed { // adjust lengths bnlength <<= 1; // double precision if (bnlength > orig_bnlength) bnlength = orig_bnlength; calc_lengths(); r = orig_r + orig_bnlength - bnlength; n = orig_n + orig_bnlength - bnlength; // bntmp1 = orig_bntmp1 + orig_bnlength - bnlength; unsafe_mult_bn(bntmp1, r, n); // bntmp1=rn inttobn(bntmp2, 1); // bntmp2 = 1.0 if (bnlength == orig_bnlength && cmp_bn(bntmp2, bntmp1+shiftfactor) == 0) // if not different break; // they must be the same inttobn(bntmp2, 2); // bntmp2 = 2.0 sub_bn(bntmp3, bntmp2, bntmp1+shiftfactor); // bntmp3=2-rn unsafe_mult_bn(bntmp1, r, bntmp3); // bntmp1=r(2-rn) copy_bn(r, bntmp1+shiftfactor); // r = bntmp1 } // restore original values bnlength = orig_bnlength; padding = orig_padding; rlength = orig_rlength; shiftfactor = orig_shiftfactor; r = orig_r; if (signflag) { neg_a_bn(r); } return r; }
static void init_bf_2(void) { int i; long ptr; save_bf_vars(); /* copy corners values for conversion */ calc_lengths(); /* allocate all the memory at once within the same segment (DOS) */ #if defined(BIG_FAR) || defined(BIG_ANSI_C) bnroot = (bf_t)MK_FP(extraseg,ENDVID); /* ENDVID is to avoid videotable */ #else /* BASED or NEAR */ bnroot = (bf_t)ENDVID; /* ENDVID is to avoid videotable */ #endif #ifdef BIG_BASED bignum_seg = (_segment)extraseg; #endif /* at present time one call would suffice, but this logic allows mulytiple kinds of alternate math eg long double */ if((i = find_alternate_math(fractype, BIGNUM)) > -1) bf_math = alternatemath[i].math; else if((i = find_alternate_math(fractype, BIGFLT)) > -1) bf_math = alternatemath[i].math; else bf_math = 1; /* maybe called from cmdfiles.c and fractype not set */ floatflag=1; /* Now split up the memory among the pointers */ /* internal pointers */ ptr = 0; bntmp1 = bnroot+ptr; ptr += rlength; bntmp2 = bnroot+ptr; ptr += rlength; bntmp3 = bnroot+ptr; ptr += rlength; bntmp4 = bnroot+ptr; ptr += rlength; bntmp5 = bnroot+ptr; ptr += rlength; bntmp6 = bnroot+ptr; ptr += rlength; bftmp1 = bnroot+ptr; ptr += rbflength+2; bftmp2 = bnroot+ptr; ptr += rbflength+2; bftmp3 = bnroot+ptr; ptr += rbflength+2; bftmp4 = bnroot+ptr; ptr += rbflength+2; bftmp5 = bnroot+ptr; ptr += rbflength+2; bftmp6 = bnroot+ptr; ptr += rbflength+2; bftmpcpy1 = bnroot+ptr; ptr += (rbflength+2)*2; bftmpcpy2 = bnroot+ptr; ptr += (rbflength+2)*2; bntmpcpy1 = bnroot+ptr; ptr += (rlength*2); bntmpcpy2 = bnroot+ptr; ptr += (rlength*2); if (bf_math == BIGNUM) { bnxmin = bnroot+ptr; ptr += bnlength; bnxmax = bnroot+ptr; ptr += bnlength; bnymin = bnroot+ptr; ptr += bnlength; bnymax = bnroot+ptr; ptr += bnlength; bnx3rd = bnroot+ptr; ptr += bnlength; bny3rd = bnroot+ptr; ptr += bnlength; bnxdel = bnroot+ptr; ptr += bnlength; bnydel = bnroot+ptr; ptr += bnlength; bnxdel2 = bnroot+ptr; ptr += bnlength; bnydel2 = bnroot+ptr; ptr += bnlength; bnold.x = bnroot+ptr; ptr += rlength; bnold.y = bnroot+ptr; ptr += rlength; bnnew.x = bnroot+ptr; ptr += rlength; bnnew.y = bnroot+ptr; ptr += rlength; bnsaved.x = bnroot+ptr; ptr += bnlength; bnsaved.y = bnroot+ptr; ptr += bnlength; bnclosenuff= bnroot+ptr; ptr += bnlength; bnparm.x = bnroot+ptr; ptr += bnlength; bnparm.y = bnroot+ptr; ptr += bnlength; bntmpsqrx = bnroot+ptr; ptr += rlength; bntmpsqry = bnroot+ptr; ptr += rlength; bntmp = bnroot+ptr; ptr += rlength; } if (bf_math == BIGFLT) { bfxdel = bnroot+ptr; ptr += bflength+2; bfydel = bnroot+ptr; ptr += bflength+2; bfxdel2 = bnroot+ptr; ptr += bflength+2; bfydel2 = bnroot+ptr; ptr += bflength+2; bfold.x = bnroot+ptr; ptr += rbflength+2; bfold.y = bnroot+ptr; ptr += rbflength+2; bfnew.x = bnroot+ptr; ptr += rbflength+2; bfnew.y = bnroot+ptr; ptr += rbflength+2; bfsaved.x = bnroot+ptr; ptr += bflength+2; bfsaved.y = bnroot+ptr; ptr += bflength+2; bfclosenuff= bnroot+ptr; ptr += bflength+2; bfparm.x = bnroot+ptr; ptr += bflength+2; bfparm.y = bnroot+ptr; ptr += bflength+2; bftmpsqrx = bnroot+ptr; ptr += rbflength+2; bftmpsqry = bnroot+ptr; ptr += rbflength+2; big_pi = bnroot+ptr; ptr += bflength+2; bftmp = bnroot+ptr; ptr += rbflength+2; } bf10tmp = bnroot+ptr; ptr += bfdecimals+4; /* ptr needs to be 16-bit aligned on some systems */ ptr = (ptr+1)&~1; stack_ptr = bnroot + ptr; startstack = ptr; /* max stack offset from bnroot */ maxstack = (long)0x10000l-(bflength+2)*22-ENDVID; /* sanity check */ /* leave room for NUMVARS variables allocated from stack */ /* also leave room for the safe area at top of segment */ if(ptr + NUMVARS*(bflength+2) > (long)0x10000l -(bflength+2)*22-ENDVID) { char msg[80]; char nmsg[80]; static FCODE fmsg[] = {"Requested precision of %d too high, aborting"}; far_strcpy(nmsg,fmsg); sprintf(msg,nmsg,decimals); stopmsg(0,msg); goodbye(); } /* room for 6 corners + 6 save corners + 10 params at top of extraseg */ /* this area is safe - use for variables that are used outside fractal*/ /* generation - e.g. zoom box variables */ ptr = ((long)0x10000l-(bflength+2)*22-ENDVID); bfxmin = bnroot+ptr; ptr += bflength+2; bfxmax = bnroot+ptr; ptr += bflength+2; bfymin = bnroot+ptr; ptr += bflength+2; bfymax = bnroot+ptr; ptr += bflength+2; bfx3rd = bnroot+ptr; ptr += bflength+2; bfy3rd = bnroot+ptr; ptr += bflength+2; for(i=0;i<10;i++) { bfparms[i] = bnroot+ptr; ptr += bflength+2; } bfsxmin = bnroot+ptr; ptr += bflength+2; bfsxmax = bnroot+ptr; ptr += bflength+2; bfsymin = bnroot+ptr; ptr += bflength+2; bfsymax = bnroot+ptr; ptr += bflength+2; bfsx3rd = bnroot+ptr; ptr += bflength+2; bfsy3rd = bnroot+ptr; ptr += bflength+2; /* end safe vars */ /* good citizens initialize variables */ if(bf_save_len) /* leave save area */ far_memset(bnroot+(bf_save_len+2)*22,0,(unsigned)(startstack-(bf_save_len+2)*22)); else /* first time through - nothing saved */ { /* high variables */ far_memset(bnroot+((long)0x10000l-(bflength+2)*22-ENDVID),0,(bflength+2)*22); /* low variables */ far_memset(bnroot,0,(unsigned)startstack); } restore_bf_vars(); /* Initialize the value of pi. Needed for trig functions. */ /* init_big_pi(); */ /* call to init_big_pi() has been moved to fractal setup routine */ /* so as to use only when necessary. */ }