New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Surprising output when server returns string with formatted double precision values. #324
Comments
Add this, and the issue is fixed:
Still, I was rather surprised that Npgsql (apparently ?) sets this parameter and thereby causes default server-side string formatting to change. |
This is pretty strange as a default behavior, am reopening for investigation even if not the highest priority... |
Thanks roji, much appreciated. |
When I run it from psql command line, I get:
I think it is not related to Npgsql specifically. I think it is more related to double precision value representation. When I set the extra_float_digits to |
Interesting. Not what I see:
That is a CentOS 6.5 server with PostgreSQL 9.3 from the yum.postgresql.org repo. So psql on the server and PgAdmin on the client agree on my machines. But Npgsql behaves differently. So something is going on in Npgsql - probably an explicit setting of extra_float_digits on connect (?). |
Yep. Npgsql changes the extra_float_digits to 3 when starting a connection in order to handle double precision numbers. In the past we had complaints that those values were being sent truncated in the last digits. |
It makes sense to me to set extra_float_digits to 3 like Npgsql does - it means you get maximum precision when floats/doubles are converted to text. What mystifies me is how that causes the 0.2 above to be printed out as 0.20000000000000001... If there were any floating point arithmetic involved it may have made sense, but with no operations? I'll try to dig into this at some point... |
There is in fact an operation involved: Conversion from decimal to binary. Only a subset of decimal floating point values can be exactly represented in the binary double precision data. That's why Postgres rounds binary float values when converting to text to provide a buest guess of the original input value.
What you get when you set extra_float_digits higher is to get all the "information" that Postgres can get from a binary float - including the representation errors.
I can see why you would want to bump extra_float_digits up to avoid rounding if you transfer binary floats as text, but wire transfer of binary floats ought to be done in binary. Not to mention the localization issues you have to work around when transferring floats as text... |
Thanks for the in-depth explanation @janholstjensen, what you say seems to make total sense to me. @glenebob (and @franciscojunior), I guess we're transfering floats in text rather than binary, right? What do you think switching to binary for that? It would allows us to stop setting the extra_float_digits to 3, leave it totally up to the user? |
Binary encoding is only used when the user prepares the command. Otherwise it is transfered as text.
The localization issue is handled by using invariant culture formatting when writing the float point value: https://github.com/npgsql/Npgsql/blob/master/Npgsql/NpgsqlTypes/NpgsqlTypeConvNativeToBackend.cs#L471 |
Ah, right, I forgot about that. So as long Postgresql simple queries, results are always given in text format, which means that floats text encoding is sensitive to extra_float_digits, right? If this is so, then unless we move to a more complex query model (either extended or simple with cursors) there's no way around this problem - except for maybe switching extra_float_digits back to its default of 0? What do you guys think? |
Isn't that more of a PostgreSQL issue than an Npgsql issue? I think it's better to leave the default value as it is, to be consistent with other client libraries. If the users really want different float representations, they can manually set extra_float_digits. |
I think I agree with @Emill here - forcing non-default Postgresql behavior on Npgsql users (i.e. increased precision) seems problematic, and the strange default behavior described in this issue is a good example (where instead of 0.3 users get 0.299999999999999989 by default). However, I admit I don't know what kind of trouble users had in the first place when the extra_float_digits wasn't set. @franciscojunior, do you remember what kind of trouble caused this in the first place? How would you feel about removing the extra_float_digits setting in Npgsql for 3.0? |
Hi all! The root of the extra_float_digits handling is this bug report about double precision numbers handling: Then, I imported Udo Liess test case to our test suite and created the Bug1010992DoubleValueSupport test: https://github.com/npgsql/Npgsql/blob/c734ed7d9de01ff32b218139175481e5366d7e03/tests/CommandTests.cs#L1328 I agree with @Emill as well and I'd add a connection string parameter which would let the user specify how many digits she wants to use? |
Hmm... The Bug1010992DoubleValueSupport test seems to make an assert that is too strict, if I understand floating point properly. In general, when comparing floats one always sets a "tolerance" (or "epsilon") value, within which differences are tolerated - see the section on float comparison in the NUnit docs. Among other things, this is because floating point arithmetic does not always yield the expected result (e.g. (x * 0.5) / 0.5 isn't necessarily exactly equal to x). In other words, I think it's perfectly acceptable for Bug1010992DoubleValueSupport to fail as it's currently written. I guess we're in agreement to stop setting extra_float_digits in Npgsql 3.0, and leave it up to the user, right? @franciscojunior, it's possible to add this as a connection string parameter but I'm not sure about the usefulness - why provide it for this parameter and not for any/all other parameters Postgresql supports? Also, connection string parameters are important for connection pooling - connections with different extra_float_digits won't be reused by the pool, although they are in essence the same... I'm not really that against an extra_float_digits connstring param, I'm just not sure it make that much sense... Let me know what you think. |
Another motivation for removing this parameter: apparent incompatibility with the pgbouncer connection pool |
No longer set extra_float_digits upon connection, let the user manage this as they see fit. Fixes npgsql#324
Issue observed first with Npgsql 2.0.14.3. Reproduced with latest 2.2.0 RC 2.
Run "select format_error();" in PgAdmin, and you see the expected result:
Do the same with Npgsql using this C# code:
and you see this error message:
Now, change the raise statement in the server function so it force-casts the double precision value to numeric before formatting:
and now we have:
So now PgAdmin and Npgsql agree. But why does the choice of client influence how the server chooses to format double precision values ?
Can anyone enlighten me as to what is going on ? And is there any way of changing this highly surprising behavior through e.g. a " set session strange_double_formats 'No'; " :-) or similar right after the connection is opened ?
Kind regards
-- Jan Holst Jensen
The text was updated successfully, but these errors were encountered: