Thread overview
real operations imprecise?
May 05, 2020
WebFreak001
May 05, 2020
H. S. Teoh
May 05, 2020
WebFreak001
May 05, 2020
kinke
May 05, 2020
I was dumping the full PI value on my machine with the highest precision it could get and got:

$ rdmd --eval='printf("%.70llf\n", PI)'
3.1415926535897932385128089594061862044327426701784133911132812500000000

now this all looks good, but when I tried to print PI_2 I got

$ rdmd --eval='printf("%.70llf\n", PI_2)'
1.5707963267948965579989817342720925807952880859375000000000000000000000

note how many more 0's there are.

When manually writing down the identifier as it is defined for PI but with the exponent reduced by one it looks good again:
$ rdmd --eval='printf("%.70llf\n", cast(real)0x1.921fb54442d18469898cc51701b84p+0L)'
1.5707963267948966192564044797030931022163713350892066955566406250000000

I would expect that the manifest constant PI_2 being defined as PI/2 would simply modify the exponent and not the actual value in such a drastic manner.

While I don't need it myself right now, is there some compiler switch to make real operations like this division more precise to not lose all these bits of information? (LDC 1.20.1)

Sure, 1^-50 precision might not be everyones typical use-case but I think there is potential improvement either in the compiler or std.math to be done here :p
May 05, 2020
On Tue, May 05, 2020 at 01:44:18PM +0000, WebFreak001 via Digitalmars-d-learn wrote:
> I was dumping the full PI value on my machine with the highest precision it could get and got:
> 
> $ rdmd --eval='printf("%.70llf\n", PI)' 3.1415926535897932385128089594061862044327426701784133911132812500000000

Whoa, hold your horses right there!  What does `pragma(msg, real.dig);`
output on your machine?

On my machine, it's 18, i.e., `real` is capable of holding only ~18 digits of meaningful information. 70 digits is WAY beyond anything that's actually represented in a `real`. And if you check online for the actual digits of pi, you'll see that after about the 19th digit of your output above, the rest of the digits are just pure garbage.  What you see is just meaningless output from the typesetting algorithm from information that isn't actually there.


> now this all looks good, but when I tried to print PI_2 I got
> 
> $ rdmd --eval='printf("%.70llf\n", PI_2)' 1.5707963267948965579989817342720925807952880859375000000000000000000000
> 
> note how many more 0's there are.
> 
> When manually writing down the identifier as it is defined for PI but
> with the exponent reduced by one it looks good again:
> $ rdmd --eval='printf("%.70llf\n",
> cast(real)0x1.921fb54442d18469898cc51701b84p+0L)'
> 1.5707963267948966192564044797030931022163713350892066955566406250000000

Again, what's the value of `real.dig` on your machine?  Don't be deceived by the number of trailing zeroes; most of the digits that come before it are complete garbage long before it dwindled to zero.  After about the 17th digit, the two printouts above have already diverged. Looks to me like it's a difference of just 1 ulp or so in the actual representation, assuming you're on x86 where `real` has about 18 digits of precision.


> I would expect that the manifest constant PI_2 being defined as PI/2 would simply modify the exponent and not the actual value in such a drastic manner.
> 
> While I don't need it myself right now, is there some compiler switch to make real operations like this division more precise to not lose all these bits of information? (LDC 1.20.1)

At the most, the above difference is only *one* bit of information. Most of your trailing digits are meaningless garbage because they don't actually exist in the representation of `real`.  Unless you have a 256-bit representation of `real`, you can't expect to get that many digits out of it!!


> Sure, 1^-50 precision might not be everyones typical use-case but I think there is potential improvement either in the compiler or std.math to be done here :p

Um, no, if you need 1^-50 precision maybe you should be looking at arbitrary-precision float libraries, like MPFR (but be prepared for a big performance hit once you move away from hardware floats). The hardware `real` type simply does not have that many bits to store that many digits. You're asking for more digits (*way* more) than are actually stored in the type, so your test results are invalid.  D's floating-point types have a .dig property for a reason.  Use it! ;-)


T

-- 
The problem with the world is that everybody else is stupid.
May 05, 2020
I can't even reproduce the 'missing' digits. On run.dlang.io, i.e., on Linux x64 (and so x87 real), I get an identical output for both DMD and LDC:

void main()
{
    import core.stdc.stdio, std.math;
    printf("%.70Lf\n", PI);
    printf("%.70Lf\n", PI_2);
    printf("%La\n", PI);
    printf("%La\n", PI_2);
}

=>

3.1415926535897932385128089594061862044327426701784133911132812500000000
1.5707963267948966192564044797030931022163713350892066955566406250000000
0xc.90fdaa22168c235p-2
0xc.90fdaa22168c235p-3
May 05, 2020
On Tuesday, 5 May 2020 at 14:15:03 UTC, H. S. Teoh wrote:
> On Tue, May 05, 2020 at 01:44:18PM +0000, WebFreak001 via Digitalmars-d-learn wrote:
>> [...]
>
> Whoa, hold your horses right there!  What does `pragma(msg, real.dig);`
> output on your machine?
>
> [...]

You are right, probably should have double checked, my fault. No idea why the numbers are different though if I specify it manually or divide by 2

Will check real.dig on that machine tomorrow but only post a reply if it's something else than 18.