You are not logged in.
Hi.
I use "Single" type to store numbers in JSON. I recently discovered that in the conversion of such numbers to the text form precision is lost (if to convert the resulting text back to a number, the result is differ from the initial).
Example:
var
v1, v2: Single;
i: integer;
s: string;
begin
i := $C120000B;
v1 := pSingle(@i)^;
s := SynCommons.ExtendedToStr(v1, SINGLE_PRECISION{ + 1});
v2 := StrToFloat(s);
if v1 <> v2 then
raise Exception.Create('Differ!');
end;
And those numbers enough numerous.
The solution is proposed here https://randomascii.wordpress.com/2012/ … -digits-2/
I propose to increase the SINGLE_PRECISION constant from 8 to 9 (I've checked, this fixes the inaccuracy). Maybe DOUBLE_PRECISION should also be increased to 17, as indicated in the article.
Last edited by Dmitro25 (2015-10-08 08:16:36)
Offline
AFAIK we followed Delphi usual precision...
The Precision parameter specifies the precision of the given value. It should be 7 or less for values of type Single, 15 or less for values of type Double, and 18 or less for values of type Extended.
IF we increase DOUBLE_PRECISION to 16 we get unexpected results like:
d := 9.999999999999997;
a[0] := AnsiChar(ExtendedToString(a,d,DOUBLE_PRECISION));
// here a='9.999999999999996, but we expected '9.999999999999997'
So I'm not convinced it is safe to increase the constants.
Online
ab
Your example contain an implicit assumption that number "9.999999999999997" can be converted to "double" type without error. But it's not. The compiler assigns to variable a value about "9.99999999999999645", so "9.999999999999996" is the best approach for it than "9.999999999999997".
You can check it with the following example:
var
v1, v2: double;
s: string;
begin
v1 := 9.999999999999997;
s := ExtendedToStr(v1, DOUBLE_PRECISION); // DOUBLE_PRECISION=15
v2 := StrToFloat(s);
if v1 <> v2 then
ShowMessage('Differ1');
s := ExtendedToStr(v1, DOUBLE_PRECISION + 1);
v2 := StrToFloat(s);
if v1 <> v2 then
ShowMessage('Differ2');
end;
You will see only "Differ1" message.
Offline
You are using StrToFloat() which convert the string to an extented 80 bit variable, so your conversion is not symmetric.
My test case was symmetric, since it compared the string value.
I doubt Delphi RTL experts (back in Borland time), did not set the appropriate precisions for the RTL, and that it was never changed after more than 20 years.
AFAIK .Net runtime made the same assumption when converting a double to text - http://stackoverflow.com/a/1664351/458259
An IEEE double has 53 significant bits (that's the value of DBL_MANT_DIG in <cfloat>). That's approximately 15.95 decimal digits (log10(253)); the implementation sets DBL_DIG to 15, not 16, because it has to round down. So you have nearly an extra decimal digit of precision (beyond what's implied by DBL_DIG==15) because of that.
I guess the reason is explained here http://stackoverflow.com/a/9999374/458259
If you have a number with 15 decimal places and convert that to a double, then print it out with exactly 15 decimal places, you should get the same number. On the other hand, if you print out an arbitrary double with 15 decimal places and the convert it back to a double, you won't necessarily get the same value back—you need 17 decimal places for that. And neither 15 nor 17 decimal places are enough to accurately display the exact decimal equivalent of an arbitrary double. In general, you need over 100 decimal places to do that precisely.
Online
You believe that a reference value for computing floating point is a string. I think that the number (binary representation) is more important. Let me explain my position. I plan to store in the database a set of coordinates in JSON format. Initially the coordinates are numbers ("Single" type). The user should be able to change the coordinates of individual points, leaving the rest unchanged. As a result of coordinate will periodically be converted from JSON (text) to numeric form and Vice versa. I am afraid that as a result of errors discussed above after multiple conversions coordinates will started to "drift" each time the user edits the data.
With increased accuracy data can be easy converted multiple times without loss of information.
Offline
I understand. Some time ago, at my request, you have added a "Single" type support when creating JSON (thank you). May be there is some way to make precision customizable without breaking the code optimization. May be to make SINGLE_PRECISION and DOUBLE_PRECISION not a constants but a variables or to provide some another interface to this values. I think, other people too may encounter the same problem, not just me.
Offline
See http://synopse.info/fossil/info/aa9734958d
Thanks for the feedback!
Online
Thank you
Offline
I've checked for "Single" type and precision=8. Difference between values before ExtendedToStr and after StrToFloat is not exceed 1LSB and have no trends to "drift".
Offline