`strtod` implementation faulty?

I’ve been integrating quickjs-ng JavaScript interpreter into SWI-Prolog.

I have a problem that when evaluating JS code 4.2 * 10 it comes back 40 within SWI-Prolog and 42 as expected when calling eval from a standalone C program.

I tracked the problem down to parsing and it seems when calling inside SWI-Prolog, the platform strtod return 4 when called with string ”4.2" (it drops all floats).

Does SWI-Prolog overwrite strtod from the C standard library with some other implementation?

My investigation here: Weird math issue when embedding to SWI-Prolog · quickjs-ng/quickjs · Discussion #1134 · GitHub

1 Like

Did some more digging, it seems to be a locale issue.

this C code in my foreign module init:

  printf("strtod(\"4.2\") == %f\n", strtod("4.2", NULL));
  printf("strtod(\"4,2\") == %f\n", strtod("4,2", NULL));

prints

strtod("4.2") == 4,000000
strtod("4,2") == 4,200000

strtod is locale dependent indeed, so you cannot use that. Instead, you can use PL_put_term_from_chars(). This parses as Prolog syntax, so it always uses the ‘.’ as float punctuation char. It can also handle arbitrary large integers, so you can support JavaScript bignums. Semantically, this parses arbitrary Prolog terms, but the implementation uses a fast track if the string is a number.

1 Like

I made an issue to quickjs-ng… it is in that JS parser implementation that calls strtod. I could work around by setting locale for the duration of my JS evaluation call.

1 Like

setlocale() is not thread-safe :frowning: You can of course use LANG=C.utf8 swipl or use Prolog’s setlocale/3 once to set the numeric locale to something that does not use ,

I should be able to use uselocale that sets current thread’s locale, and then reset it back after evaluation.

1 Like

I see. I guess SWI-Prolog’s locale handling needs some updating as modern C libraries to provide better things than good old setlocale(). I was not aware of uselocale(). Learned something new :slight_smile:

1 Like

After calling strtod(), you should check that endptr points to the position after the number. (In OP’s example, 4.2would have resulted in endptr pointing to the . if 4.0 was returned, and to the ’\0’ after the 1 if 4.2 was returned)