On Friday, 20 May 2016 at 11:02:45 UTC, Timon Gehr wrote:
On 20.05.2016 11:14, Joakim wrote:
On Thursday, 19 May 2016 at 18:22:48 UTC, Timon Gehr wrote:
On 19.05.2016 08:04, Joakim wrote:
On Wednesday, 18 May 2016 at 17:10:25 UTC, Timon Gehr wrote:
It's not just slightly worse, it can cut the number of useful bits in half or more! It is not unusual, I have actually run into those problems in the past, and it can break an algorithm that is in Phobos
today!

I wouldn't call that broken. Looking at the hex output by replacing %f with %A in writefln, it appears the only differences in all those
results is the last byte in the significand.

Argh...

// ...

void main(){
    //double[] data=[1e16,1,-9e15];
    import std.range;
    double[] data=1e16~repeat(1.0,100000000).array~(-9e15);
    import std.stdio;
    writefln("%f",sum(data)); // baseline
    writefln("%f",kahan(data)); // kahan
    writefln("%f",kahanBroken(data)); // broken kahan
}


dmd -run kahanDemo.d
1000000000000000.000000
1000000100000000.000000
1000000000000000.000000

dmd -m32 -O -run kahanDemo.d
1000000000000000.000000
1000000000000000.000000
1000000000000000.000000


Better?

Obviously there is more structure in the data that I invent manually than in a real test case where it would go wrong. The problems carry
over though.

I looked over your code a bit. If I define sum and c as reals in
"kahanBroken" at runtime, this problem goes away.

Yes. That's absolutely obvious, and I have noted it before, but thanks. Maybe try to understand why this problem occurs in the first place.

Yet you're the one arguing against increasing precision everywhere in CTFE.

Since that's what the
CTFE rule is actually doing, ie extending all floating-point to reals at compile-time, I don't see what you're complaining about. Try it, run even your original naive summation algorithm through CTFE and it will
produce the result you want:

enum double[] ctData=[1e16,1,-9e15];
enum ctSum = sum(ctData);
writefln("%f", ctSum);
...

This example wasn't specifically about CTFE, but just imagine that only part of the computation is done at CTFE, all local variables are transferred to runtime and the computation is completed there.

Why would I imagine that? And this whole discussion is about what happens if you change the precision of all variables to real when doing CTFE, so what's the point of giving an example that isn't "specifically" about that?

And if any part of it is done at runtime using the algorithms you gave, which you yourself admit works fine if you use the right higher-precision types, you don't seem to have a point at all.

As Don's talk pointed out,
all floating-point calculations will see loss of precision starting
there.
...


This is implicitly assuming a development model where the programmer first writes down the computation as it would be correct in the real
number system and then naively replaces every operation by the
rounding equivalent and hopes for the best.

No, it is intrinsic to any floating-point calculation.
...

How do you even define accuracy if you don't specify an infinitely precise reference result?

There is no such thing as an infinitely precise result. All one can do is compute using even higher precision and compare it to lower precision.

It is a useful rule if that is what you're doing. One might be doing something else. Consider the following paper for an example where the last bit in the significant actually carries useful information for
many of the values used in the program.

http://www.jaist.ac.jp/~s1410018/papers/qd.pdf

Did you link to the wrong paper? ;)

No. That paper uses multiple doubles per approximated real value to implement arithmetic that is more precise than using just plain doubles. If any bit in the first double is off, this is no better than using a single double.

I skimmed it and that paper
explicitly talks about error bounds all over the place.

It is enough to read the abstract to figure out what the problem is. This demonstrates a non-contrived case where CTFE using enhanced precision throughout can break your program. Compute something as a double-double at compile-time, and when it is transferred to runtime you lose all the nice extra precision, because bits in the middle of the (conceptual) mantissa are lost.

That is a very specific case where they're implementing higher-precision algorithms using lower-precision registers. If you're going to all that trouble, you should know not to blindly run the same code at compile-time.

The only mention of "the last bit" is

This part is actually funny. Thanks for the laugh. :-)
I was going to say that your text search was too naive, but then I double-checked your claim and there are actually two mentions of "the last bit", and close by to the other mention, the paper says that "the first double a_0 is a double-precision approximation to the number a, accurate to almost half an ulp."

Is there a point to this paragraph?

when they say they calculated their
constants in arbitrary precision before rounding them for runtime use, which is ironically similar to what Walter suggested doing for D's CTFE
also.
...

Nothing "ironic" about that. It is sometimes a good idea and I can do this explicitly and make sure the rounding is done correctly, just like they did. Also, it is a lot more flexible if I can specify the exact way the computation is done and the result is rounded. 80 bits might not be enough anyway. There is no reason for the language to apply potentially incorrect "hand holding" here.

Again, please understand that my point is not that lower precision is better. My point is that doing the same thing in every context and allowing the programmer to specify what happens is better.

I understand your point that sometimes the programmer wants more control. But as long as the way CTFE extending precision is consistently done and clearly communicated, those people can always opt out and do it some other way.

In this case, not increasing precision gets the more accurate result, but other examples could be constructed that _heavily_ favor increasing
precision.

Sure. In such cases, you should use higher precision. What is the problem? This is already supported (the compiler is not allowed to used
lower precision than requested).

I'm not the one with the problem, you're the one complaining.
...

So you see no problem with my requested semantics for the built-in floating point types?

I think it's an extreme minority use case that may not merit the work, though I don't know how much work it would require. However, I also feel that way about Walter's suggested move to 128-bit CTFE, as dmd is x86-only anyway.

Reply via email to