-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
WIP: json_encode
, and vim_snprintf
, loses floating point precision when using %g
#15902
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
base: master
Are you sure you want to change the base?
Conversation
Warning: this breaks `json_decode`, and does less than good things to `json_encode` Vim functions! ```vim echo json_encode({'value':0.5670403838157654}) ``` > Results could be; > > - `{"value":5.068898e-310}` > - `{"value":1.237282e-313}` > - `{"value":0.0}` ```vim :echo json_decode('{"value":0.5670403838157654}') ``` > Results, regardless of float size, always be; > > - `{'value': 0.0}`
I can't find it back in the Vim help, but IIRC |
It currently is at |
Ah, thanks. Yes, this is what I meant. |
Isn't that just a display issue only? In which case I think we don't even need to worry about? |
I agree that Double-checking myself on Vim version let json_stringy = '{ "value": "0.009260174818336964" }'
let json_floater = '{ "value": 0.009260174818336964 }'
let parsed_stringy = printf('%s', json_decode(json_stringy).value)
let parsed_floater = printf('%.18g', json_decode(json_floater).value)
echo parsed_stringy == parsed_floater
"> 1
echo len(parsed_stringy)
"> 20
echo len(parsed_floater)
"> 20 ... But things get funky when re-encoding; echo json_encode(json_decode(json_stringy))
"> {"value":"0.009260174
8000
818336964"}
echo json_encode(json_decode(json_floater))
"> {"value":0.00926} So I may need to revert previously proposed changes and instead fixate on what UpdateI reverted changes, because it looks like the
vim_snprintf((char *)numbuf, NUMBUFLEN, "%g",
val->vval.v_float); ... And indeed hard-coding an explicitly long length gets me closer to joy, eg. - vim_snprintf((char *)numbuf, NUMBUFLEN, "%g",
+ vim_snprintf((char *)numbuf, NUMBUFLEN, "%.20g", After re-compiling, tests show only the last digit is failing to retain precision!!! let json_floater = '{ "value": 0.009260174818336964 }'
echo json_encode(json_decode(json_floater))
"> {"value":0.00926017481833696365} Digging deeper into the source it looks like
... But at this moment I'm not feeling clever enough for that. Side noteFor those following along at home, here are the configuration and make commands I've been using; cd ~/git/hub/vim/vim
./configure --enable-gui=gtc3\
--enable-luainterp=yes\
--enable-pythoninterp=yes\
--with-python-config-dir="$(python-config --configdir)"\
--enable-python3interp=yes\
--with-python3-config-dir="$(python3-config --configdir)"\
--enable-perlinterp=yes\
--enable-rubyinterp=yes\
--enable-cscope\
--enable-farsi\
--enable-fontset\
--enable-gpm\
--enable-largefile\
--enable-multibyte\
--enable-rightleft\
--enable-terminal\
--enable-xim\
--with-features=huge\
--with-compiledby='S0AndS0'\
--prefix='/usr/local' _last_version="$(git tag --list | tail -n1)"
_date_time="$(date +%Y%m%d_%H%M%S)"
_new_version="${_last_version}-${_date_time}"
make -j$(nproc) VIMRUNTIMEDIR="/usr/local/share/vim/vim_${_new_version}" I think at this point the big questions I have are;
Update 2I figured it'd be easier to play around with possible dynamic precision via a separate file; #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NUMBUFLEN 65
#define MIN(x, y) ( (x < y) ? x : y )
/**
* Attributions:
* - https://stackoverflow.com/questions/3437404/min-and-max-in-c
* - https://stackoverflow.com/questions/1745726/how-to-store-printf-into-a-variable
* - https://stackoverflow.com/questions/4824/string-indexof-function-in-c
*/
int main() {
// Assume we have conversions to and fro
double f = 0.41968;
char *string = "0.41968";
// Get the number of characters beyond decimal point
char *ptr = strchr(string, '.');
int index = ptr - string;
int float_size = strlen(string) - (index + 1);
// Create a printf compatible format string
int precision = MIN(NUMBUFLEN, float_size);
char *buf;
size_t sz;
sz = snprintf(NULL, 0, "%%.%ig", precision);
buf = (char *)malloc(sz + 1);
snprintf(buf, sz+1, "%%.%ig", precision);
// Debugging stuff commented out
/* printf("Index: %i\nFloat size: %i\n", index, float_size); */
/* printf("Precision: %i\n", precision); */
/* printf("Format: %s\n", buf); */
// Show we got the float and no more!
printf(buf, f);
printf("\n");
return 0;
} ... And though it works, I'm not sure it really sparks joy from a future maintenance perspective 🤔 Update 3After much fiddling about on my end it looks as though dynamically defining Also the whole dynamic sizing thing is likely to be a point of future frustration, for various reasons, so at this point I'm thinking a data-structure may be the safest way of preventing loss of precision; though likely at the cost of more memory. |
long double
type for float_T
json_encode
, and vim_snprintf
, loses floating point precision when using %g
Manual testing shows this seems to _mostly_ work... _mostly_... Mainly it fails when echoing a dictionary; ```vim echo {'floater': 0.419} ``` > `{'floater': 0.00000000000000000000}` ... But, partially succeeds when accessing a value!? ```vim echo {'floater': 0.419}.floater ``` > `6.95323265041282803632e-310` On a positive note, we can now use `%L` with `f`, `g`, and friends! ```vim echo printf('%.18Lg', {'value':0.00926017481833696365}.value) ``` > `0.009260174818336964` ... Which proves some of these changes function as intended X-D
Please do not change version.c in a PR |
I think I got most of the bits for allowing Only bit that is being a stinker are print/echo-ing dictionaries as a whole, eg. echo {'floater': 0.419}
"> `{'floater': 0.00000000000000000000}`
echo {'floater': 0.419}.floater
"> `6.95323265041282803632e-310` ... But if feels close to doable, closer at least, and it seems echo printf('%.18Lg', {'value':0.00926017481833696365}.value)
"> `0.009260174818336964` TLDR I'd much appreciate a pointer to where I'm messing thing up when a dictionary'' |
Uh-oh 🤦 ... I'll try to get that reverted soon EditOkay I think after a bit of |
0d6b7c1
to
dc05bdb
Compare
@@ -6891,7 +6891,16 @@ | |||
get_var_special_name(iptr->isn_arg.number)); | |||
break; | |||
case ISN_PUSHF: | |||
smsg("%s%4d PUSHF %g", pfx, current, iptr->isn_arg.fnumber); | |||
#if defined(__LP64__) | |||
smsg("%s%4d PUSHF %.*Lg", FLOAT_PRECISION, pfx, current, |
Check failure
Code scanning / CodeQL
Wrong type of arguments to formatting function High
echo
and messes with other Vim Ex mode commands, ex;... Side note;
printf
within Vim Ex mode also has inconsistent behaviors.Questions
e
notation?Story time
I'm writing a plugin, aimed at Vim8 compatibility, that makes calls to an API that I do not control outputs of. And the API returns JSON that contains large floats, example;
... As of Vim version
9.1.1-785
packaged by Arch, I use Arch (BTW™), these values are truncated to about five digits past the decimal point, eg.Oddly it looks like this might be a
echo
bug, because wrapping withprintf
and using a large limit mostly preserves precision of input floats... mostly