Hacker News new | ask | show | jobs
by pklausler 3400 days ago
3.141592653589793115997963468544185161590576171875 is the exact decimal representation for the 64-bit IEEE-754 number that's closest to pi (viz. 0x400921FB54442D18).

Any time you see a decimal floating-point constant with a nonzero fractional part that doesn't end in '5', you're looking at a bug.

EDIT: As long as this grizzled old Fortran programmer is giving out free advice, I'll add two more items every programmer should know about binary floating-point:

a) Every binary floating-point number can be represented exactly in decimal notation if you use enough digits.

b) Those decimal values are the only ones that can be exactly converted to binary; all of the rest require rounding.

4 comments

> Any time you see a decimal floating-point constant with a nonzero fractional part that doesn't end in '5', you're looking at a bug.

That's just silly. If you're writing some famous mathematical constant, the digits should match that constant, and not the requirements of the machine. (Except for the last one being rounded off.) Suppose we had a floating-point machine that gave us maximum 4 digits of decimal precision. I wouldn't define the PI constant as 3.145. That would just look like a typo to people who have PI memorized to half a dozen digits or more. I'd make it 3.14159 (or more) and let the darn compiler find the nearest approximation on the floating-point axis.

Any exact decimal representation of a specific binary floating-point number that's finite and not an integer must end in the digit '5' (perhaps with trailing zeroes). This is because its fractional part is (the sum of) a set of powers of two with negative exponents, and their exact decimal representations (0.5, 0.25, 0.125, &c.) all end in '5' (proof by induction is obvious and left to the reader).
Any time you see a decimal floating-point constant with a nonzero fractional part that doesn't end in '5', you're looking at a bug.

depends on the language. for example here it is in go:

    Pi  = 3.14159265358979323846264338327950288419716939937510582097494459 // http://oeis.org/A000796
What the person above you is saying, I think, is to remember that computers usually work in base 2. This applies to IEEE floating points, where the mantissa is in base 2; when you represent fractions in base two, they're powers of two: 1/2 (.5), 1/4 (.25), 1/8 (.125), etc. What he's asserting, I think, is that any power-of-two fraction, or any combination of those (in binary), result in a number ending in 5 when represented in decimal. Anything else is going to be rounded to the nearest representable number (that ends in 5).

So, go might have that value in its source, but it's getting rounded to something that would, if represented in decimal, end in 5.

In Go, floating-point constants may have very high precision, so that arithmetic involving them is more accurate. The constants defined in the math package are given with many more digits than are available in a float64.

Having so many digits available means that calculations like Pi/2 or other more intricate evaluations can carry more precision until the result is assigned, making calculations involving constants easier to write without losing precision. It also means that there is no occasion in which the floating-point corner cases like infinities, soft underflows, and NaNs arise in constant expressions.

There's an argument to be made the other way too. If you're using an unusual default rounding direction and you care which direction your PI constant is rounded, you might prefer it if PI rounded in the same rounding direction as the rest of your floating point math. In that case you'd want a constant that is equivalent to PI under all rounding modes.
The code below works for me

#define PI1 3.141592653589793115997963468544185161590576171875

double pi1 = PI1;

#define PI2 3.14159265358979323846264338327950288

double pi2 = PI2;

assert(pi1 == pi2);

(edit: or even 3.14159265358979323846)

Your PI2 rounds to PI1 under the rounding mode used at compilation time. Print it out with "%50.48f" (or FORMAT(F50.48)) and you'll see PI1. But PI1 is independent of rounding mode.
Sure. I thought you were implying more than that. When you called it a bug I thought you were implying that the use of one over another would alter the output of a program. My bad.