digitalmars.D.learn - real operations imprecise?
- WebFreak001 (22/22) May 05 2020 I was dumping the full PI value on my machine with the highest
- H. S. Teoh (32/59) May 05 2020 Whoa, hold your horses right there! What does `pragma(msg, real.dig);`
- WebFreak001 (6/13) May 05 2020 You are right, probably should have double checked, my fault. No
- kinke (16/16) May 05 2020 I can't even reproduce the 'missing' digits. On run.dlang.io,
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.1415926535897932385128089594061862044327426701784133911132812500000000Whoa, 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.5707963267948966192564044797030931022163713350892066955566406250000000Again, 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 :pUm, 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
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: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.[...]Whoa, hold your horses right there! What does `pragma(msg, real.dig);` output on your machine? [...]
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