Question

I would like to use the BBP formula to calculate Pi in a C-program's pthread process while another process prints the result for as far as it has got. However BBP gives a base 16 answer while I would like to stream a base 10 answer to the user.

How can I determine whether it's safe to print the n-th digit of a base 10 converted base 16 number?

Thanks in advance!

Was it helpful?

Solution

One solution is to test whether increasing the last hexadecimal digit currently available changes the decimal digit you are considering displaying.

Consider a number x with hexadecimal representation …h3h2h1h0.h-1h-2… and decimal representation …d3d2d1d0.d-1d-2

Suppose we have a truncated numeral, so that we know only the digits from h to hj. Let y be the number represented by these digits. Let z be y + 16j, which is y plus one in the j digit position. Then the value of x might be any value from y (inclusive) to z (exclusive).

Now consider a candidate decimal numeral, with digits d to di. Let y' be the number represented by these digits. Let z' be y + 10i. Iff y' ≤ y and z ≤ z', then the decimal digits d to di must be a prefix of the complete decimal numeral for x (that is, these decimal digits are known to appear in the decimal numeral for x; they will not change as more hexadecimal digits are discovered).

This is because the value of x, being in [y, z), can be formed by adding some zero or positive value to y' and that value needed is less than 1 in the i digit position. Conversely, if the inequalities do not hold, then x could be outside the interval spanned by the candidate digits.

OTHER TIPS

@Eric Postpischil posted a fine general purpose algorithm.

In implementing OP goal's, some short-cuts may be realized.
Handle the integer portion of Pi separate and only deal with the fraction.
Assume input is base 16 and then add 1 bit at a time.

Implementation notes:
I cheated by using fixed memory allocation and byte-array (string) handling. Certainly one would save the array length rather than strlen() and use byte 0 - 9 rather than char '0' to '9', but this was tossed together quickly and was easier to debug this way. Array size s/b dynamic, but that is easy to add.

#include <assert.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct {
  char *Sum;
  char *Add;
} Pi_T;

void Pi_Print(const char *Title, Pi_T *State) {
  printf("%s\n", Title);
  printf("  Sum: '%s'\n", State->Sum);
  printf("  Add: '%s'\n", State->Add);
}

// Sum += Add
void Pi_Add(char *Sum, char *Add) {
  size_t LenS = strlen(Sum);
  size_t LenA = strlen(Add);
  while (LenS > LenA) {
    Add[LenA++] = '0';
    Add[LenA] = '\0';
  }
  while (LenA > LenS) {
    Sum[LenS++] = '0';
    Sum[LenS] = '\0';
  }
  unsigned Accumulator = 0;
  while (LenA > 0) {
    LenA--;
    Accumulator += Add[LenA] - '0';
    Accumulator += Sum[LenA] - '0';
    Sum[LenA] = Accumulator % 10 + '0';
    Accumulator /= 10;
    assert(Accumulator <= 9);
  }
  assert(Accumulator == 0);
}

// Divide the `Add` by 2
void Pi_Div2(char *Add) {
  size_t LenS = strlen(Add);
  size_t i;
  unsigned Accumulator = 0;
  for (i = 0; i < LenS; i++) {
    Accumulator += Add[i] - '0';
    Add[i] = Accumulator / 2 + '0';
    Accumulator %= 2;
    Accumulator *= 10;
    assert ((Accumulator == 0) ||  (Accumulator == 10));
  }
  if (Accumulator > 0) {
    Add[i++] = Accumulator / 2 + '0';
    Add[i] = '\0';
    Accumulator %= 2;
    Accumulator *= 10;
    assert(Accumulator == 0);
  }
}

void Pi_PutHex(Pi_T *State, unsigned HexDigit) {
  // Add HexDigit, 1 bit at a time.
  for (unsigned i = 4; i-- > 0;) {
    if (HexDigit & (1 << i)) {
      Pi_Add(State->Sum, State->Add);
    }
    // Should the Sum[0] be extracted?
    if (State->Add[0] == '0') {
      for (size_t i = 1; State->Sum[i] && State->Add[i]; i++) {
        unsigned Accumulator = State->Sum[i] - '0' + State->Add[i] - '0';
        if (Accumulator > 9)
          break;
        if (Accumulator < 9) {

          // !!!!!!!!!!!!!!!!!!!!!!!!!!!!
          // Print the decimal digit!
          printf("%c", State->Sum[0]);
          // !!!!!!!!!!!!!!!!!!!!!!!!!!!!

          memmove(&State->Sum[0], &State->Sum[1], strlen(State->Sum));
          memmove(&State->Add[0], &State->Add[1], strlen(State->Add));
          break;
        }
      }
    }
    Pi_Div2(State->Add);
  }
}

void Pi_Test(void) {
  Pi_T State;
  State.Sum = malloc(500);
  State.Add = malloc(500);
  State.Sum[0] = '\0';
  State.Add[0] = '5';
  State.Add[1] = '\0';
  // http://calccrypto.wikidot.com/math:pi-hex
  static const char *pi = "3.243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89452821E638D01378";
  // http://www.miniwebtool.com/first-n-digits-of-pi/?number=100
  // 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679
  // Output
  // 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117
  const char *p = &pi[2];
  // Pi_Print("Init", &State);
  printf("3.");
  // add each hex digit, one at a time.
  while (*p) {
    unsigned HexDigit = (*p <= '9')  ? (*p - '0') : (*p - 'A' + 10);

    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // Put in the hexadecimal digit
    Pi_PutHex(&State, HexDigit);
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!

    p++;
  }
  printf("\n");
  // Pi_Print("End", &State);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top