3:27 AM
In this page (which implements IEEE754 with arbitrary precision math -- i.e. probably no error) enter a decimal number of "0.3333333333333333333333333333" (yes a lot of digits) and the resulting double decimal with 21 digits will be "0.333333333333333314829" which clearly is not equal to "0.33…". So, no, not the same.
6 hours later…
9:15 AM
@isaac note that it's 21 decimal digits. Those 0x1.555555p-2 representations use hexadecimal for the mantis and decimal for the (power of 2) exponent.
`#include <stdlib.h>
#include <stdio.h>
void printld(long double d) {
unsigned char *p = (unsigned char*) &d;
for (int i = 0; i < sizeof(d); i++)
printf("%02x", p[i]);
printf(" %La %.80Lg\n", d, d);
}
int main(int argc, char *argv[]) {
long double x = 1.L/3L, y;
y = strtold(argv[1], 0);
printld(x);
printld(y);
return 0;
}
`
#include <stdio.h>
void printld(long double d) {
unsigned char *p = (unsigned char*) &d;
for (int i = 0; i < sizeof(d); i++)
printf("%02x", p[i]);
printf(" %La %.80Lg\n", d, d);
}
int main(int argc, char *argv[]) {
long double x = 1.L/3L, y;
y = strtold(argv[1], 0);
printld(x);
printld(y);
return 0;
}
`
@isaac for double, you need 17 digits. 21 digits is for the 80bit extended precision ones. That yields 0.33333333333333331 for 1/3 which is what zsh gives you. If you remove the trailing 31 (like yash does), that becomes a different floating number that is further away from 1/3 than 0.33333333333333331 is (0.333333333333332981762708868700428865849971771240234375)
If you only remove only the 1, that yields the same number. OK that time, but if you change it to 5/9 for instance, 0.5555555555555555 yields a different number than 0.55555555555555558 which is further away (0.55555555555555546920487586248782463371753692626953125 vs 0.5555555555555555802271783250034786760807037353515625)
IOW, even though 0.55555555555555558 looks worse than 0.5555555555555555, it is closer to the truth (5/9)
7 hours later…
4:36 PM
5:11 PM
Please understand that even if the long double is a 16 byte (128 bit) entity it is only an 80 bit float. Read in wikipedia With the GNU C Compiler, long double is 80-bit extended precision on x86 processors regardless of the physical storage used for the type (which can be either 96 or 128 bits). And I am compiling the code in linux, not an SPARC or a PowerPC.
An 80 bit float only has 64 bit for the mantissa. A 64 bit mantissa has an effective range of precision of around 19 digits
awk 'BEGIN{print(1/2^64)}'
yields 5.42101e-20
5:32 PM
What the 21 digits is trying to mean is that if you start with a given decimal with 21 digits (lets use 0.5555555555555555555555) you will end with the exact same decimal after two conversions: to binary and back to decimal if the IEEE754 implementation is correct. Try this code with
program 0.5555555555555555555555
to see the results.
<pre>```
#include <stdlib.h>
#include <stdio.h>
void printld(long double d) {
unsigned char *p = (unsigned char*) &d;
for (int i = 0; i < sizeof(d); i++)
printf("%02x", p[i]);
printf(" %La %.22Lg\n", d, d);
}
int main(int argc, char *argv[]) {
long double x = 1.L/3L, y = 0.L;
if (argc == 2 ) y = strtold(argv[1], 0);
printf("A long double is an entity of = %lu bytes\n",sizeof(x));
printld(x);
if (argc == 2 ) printld(y);
return 0;
}
```</pre>
#include <stdlib.h>
#include <stdio.h>
void printld(long double d) {
unsigned char *p = (unsigned char*) &d;
for (int i = 0; i < sizeof(d); i++)
printf("%02x", p[i]);
printf(" %La %.22Lg\n", d, d);
}
int main(int argc, char *argv[]) {
long double x = 1.L/3L, y = 0.L;
if (argc == 2 ) y = strtold(argv[1], 0);
printf("A long double is an entity of = %lu bytes\n",sizeof(x));
printld(x);
if (argc == 2 ) printld(y);
return 0;
}
```</pre>
But in trying the program with 21 digits (0.555555555555555555555) yields 0.555555555555555555561 (the same 21 digits). That means that the implementation is not a faithful implementation of IEEE754 in that it fails the full round trip.
Note however that the 21 digits 0.333333333333333333333 you propose (use program 0.333333333333333333333 to see it) has the same binary representation that a 1/3 has. That is the maximum precision possible: 0.3333333333333333333424
That is why the very long number you present here: (0.333333333333332981762708868700428865849971771240234375) has absolutely NO meaning. That's why it deviates from a repeated 3 just after only 14 digits (what follows is a 2981… etc). It is not a correct 128 bit IEEE754 float, it is an extended 80bit float. No more than 21 decimal digits have sense or may mean something. Using a printf of 80 decimal digits (%.80Lg) outputs a lot of digits, but mean nothing real, are only noise of some code.
2 hours later…
8:46 PM
@isaac It looks like I'm failing to communicate what I mean to you. I may add a Q&A when I have a moment explaining why zsh gives the result of floating point arithmetic expansions with 17 significant digits and why it has good reasons to do so even if that causes those artefacts. I agree the discussion is over here.
Nor should with 17 digits of 198.4. Even the gcc implementation of IEEE754 which is not correct as we already found out produce 20 reasonably correct digits (198.39999999999999999) (try program 198.4). Trimming it to 17 digits should round up the last 9 to produce 198.4 (round trip correct). It doesn't.
« first day next day → last day (18 days later) »
Transcript for
Feb1
Feb '182
Feb4
Discussion between isaac and Stéphane…
Imported from a comment discussion on unix.stackexchange.com/q...