coder-mike / microvium

A compact, embeddable scripting engine for applications and microcontrollers for executing programs written in a subset of the JavaScript language.
MIT License
569 stars 25 forks source link

mvm_toStringUtf8 fails on float numbers #68

Closed boogie closed 1 year ago

boogie commented 1 year ago

I am working with numbers, sometimes I have a float to console.log. Currently the microvium code aborts when I'm calling mvm_toStringUtf8 on a float number. Which might be OK, and I'm looking for a workaround now.

It seems that internally vm_convertToString returns 0xFFFF instead of a string pointer, and then an assert fails.

I'm trying to check the type of the value before calling mvm_toStringUtf8 to recognize if it's a float number, but it returns a generic number type, so I cannot decide if it's an integer or float. I've patched the code now and mvm_toStringUtf8 returns "[Float]", but maybe you can recommend a better workaround?

coder-mike commented 1 year ago

In terms of detecting if the number is a float, you could probably use ((n | 0) !== n). The | 0 is a fairly idiomatic way of coercing a JavaScript number to a signed 32-bit integer.

If you're looking for a way to check in C, there isn't really a way to check if it's an int32, but you can check if it's a 14-bit integer using ((value & 3) == 3). Depending on the range of values you're looking at, this may or may not work for you.

But keep this ticket open, because the 0xFFFF thing sounds like a bug. Also it would be good to support conversion of float to string at some point.

boogie commented 1 year ago

If it helps, I've used gcvt to implement a quick and dirty float to string conversion:

static Value vm_floatToStr(VM* vm, MVM_FLOAT64 n) {
  char buf[64];
  memset(buf, 0, sizeof(buf));
  gcvt(n, 10, buf);
  return mvm_newString(vm, buf, strlen(buf));
}

I'm not sure about the maximum length of the gcvt output, and perhaps buf[0] = 0 would be enough instead of the memset. Or error checking.

I've thought about implementing the conversion myself, however gcvt supports the "e-notation" as well. Also I'm not sure how important it is, but maybe it would be great if Microvium would produce the same string format as standard JavaScript. JavaScript also cares about locale, which seems to be not important for Microvium.

coder-mike commented 1 year ago

gcvt is not part of the C standard library. But I could use snprintf. Something like the following:

static void vm_float64ToStr(double x, char *buffer, size_t buffer_size) {
    if (isnan(x)) {
        snprintf(buffer, buffer_size, "NaN");
    } else if (isinf(x)) {
        snprintf(buffer, buffer_size, (x > 0) ? "Infinity" : "-Infinity");
    } else {
        snprintf(buffer, buffer_size, "%.15g", x);
    }
}
coder-mike commented 1 year ago

Microvium now supports string formatting of floating point numbers.

function testIntToString() {
  assertEqual('' + 0, '0');
  assertEqual('' + 1, '1');
  assertEqual('' + -1, '-1');
  assertEqual('' + (0x7FFFFFFF), '2147483647');
  assertEqual('' + (-0x80000000), '-2147483648');
}

function testFloatToString() {
  assertEqual('' + NaN, 'NaN');
  assertEqual('' + Infinity, 'Infinity');
  assertEqual('' + (-Infinity), '-Infinity');
  assertEqual('' + (-0.0), '0');
  assertEqual('' + 0.1, '0.1');
  assertEqual('' + (-0.1), '-0.1');
  assertEqual('' + 1e30, '1e+30');
  assertEqual('' + (-1e30), '-1e+30');
  assertEqual('' + 1e-30, '1e-30');
  assertEqual('' + (-1e-30), '-1e-30');
}

I haven't specifically tried with mvm_toStringUtf8 but it should hit the same code paths and have the same behavior.