Calculate pi using "infinite precision" numbers, and you get it to infinite precision. Obviously you can't print out the whole answer - but you can print out as much of the answer as you want, until the machine runs out of memory or time.
An "infinite precision number" is usually held as a list of digits, along with a method for calculating more digits. You can then write functions that can add, subtract, multiply and divide such numbers. It is particularly convenient to use a functional programming language for such tasks, as they are good at working with unlimited lists, and with manipulating functions (such as the digit generator function).
To calculate pi, the easiest way is to note that atan(1) = pi/4, and atan(x) = x - x^3 / 3 + x^5 / 5 - x^7 / 7 + ... Once you have the individual parts in place, the rest is easy. Of course, you end up with lots of unlimited lists of unlimited lists, etc.
Mathematically speaking, what you have is precise representation of /all/ the digits of pi. You can't display them all, as you have limited ram and time - but it /is/ precise and contains /all/ the digits - just as "0.33..." is a precise decimal representation of /all/ the digits of
1/3 (the "..." gives the method of producing any digits you want).