espruino / Espruino

The Espruino JavaScript interpreter - Official Repo
http://www.espruino.com/
Other
2.73k stars 741 forks source link

Math.sqrt e.compiledC() #2444

Closed d3nd3 closed 5 months ago

d3nd3 commented 6 months ago

When I attempt to call the javascript function Math.sqrt() from within here, it returns the same argument I gave it. eg. In 64, out 64.

float doubleToFloat(double value) {
    union {
        double d;
        unsigned long long u64;
    } doubleUnion;

    union {
        float f;
        unsigned int u32;
    } floatUnion;

    doubleUnion.d = value;
    int sign = (doubleUnion.u64 >> 63) & 1;
    int exponent = (int)((doubleUnion.u64 >> 52) & 0x7FF);
    unsigned long long fraction = doubleUnion.u64 & 0xFFFFFFFFFFFFF;

    if (exponent == 0x7FF) {
        floatUnion.u32 = (sign << 31) | 0x7F800000 | ((fraction >> 29) & 0x007FFFFF);
    } else if (exponent == 0) {
        int shift = 1023 - 127;  // Double bias - Float bias
        unsigned int denormalized_exp = 1 - shift;
        fraction = fraction >> shift;
        floatUnion.u32 = (sign << 31) | (denormalized_exp << 23) | ((fraction >> 29) & 0x007FFFFF);
    } else {
        exponent -= 1023;  // Double bias
        if (exponent > 127) {
            exponent = 127;
        } else if (exponent < -126) {
            exponent = -126;
        }
        floatUnion.u32 = (sign << 31) | ((exponent + 127) << 23) | ((fraction >> 29) & 0x007FFFFF);
    }
    return floatUnion.f;
}

double floatToDouble(float value) {
    union {
        float f;
        unsigned int u32;
    } floatUnion;
    union {
        double d;
        unsigned long long u64;
    } doubleUnion;
    floatUnion.f = value;
    int sign = (floatUnion.u32 >> 31) & 1;
    int exponent = (floatUnion.u32 >> 23) & 0xFF;
    unsigned int fraction = floatUnion.u32 & 0x7FFFFF;

    if (exponent == 0xFF) {
        doubleUnion.u64 = ((unsigned long long)exponent << 52) | ((unsigned long long)fraction << 29);
    } else if (exponent == 0) {
        int shift = 1;
        while ((fraction & 0x40000000) == 0) {
            fraction <<= 1;
            shift++;
        }
        exponent -= (127 + shift - 1);
        doubleUnion.u64 = (sign << 63) | ((unsigned long long)exponent << 52) | ((unsigned long long)fraction << 29);
    } else {
        exponent += (1023 - 127);
        doubleUnion.u64 = (sign << 63) | ((unsigned long long)exponent << 52) | ((unsigned long long)fraction << 29);
    }

    return doubleUnion.d;
}
float sqrt(float infloat) {
  JsVar * G = jspGetNamedVariable("global");
  //JsVar * M = jspGetNamedField(G,"Math",false);
  //JsVar * M = jspGetNamedVariable("Math");
  //JsVar * sqrtt = jspGetNamedField(M,"sqrt",false);
  JsVar * sqrtt = jspGetNamedField(G,"squareroot",false);

  //JsVar *f = jsvNewFromFloat(floatToDouble(infloat));
  JsVar *f = jsvNewFromFloat(360.0);

  JsVar * res = jspeFunctionCall(sqrtt,0,0,false,1,&f);
  double d = jsvGetFloat(res);

  float outfloat = doubleToFloat(d);
  outfloat = outfloat / 8;
  outfloat = doubleToFloat(floatToDouble(outfloat));
  jsvUnLock(res);
  jsvUnLock(f);
  jsvUnLock(sqrtt);
  //jsvUnLock(M);
  jsvUnLock(G);

  return outfloat;
}
gfwilliams commented 6 months ago

Do you think you could provide a full example that I can just paste into the IDE and try?

For example if I try to strip off all the extra stuff:

// this fails with: Uncaught Error: Function not found!
var c = E.compiledC(`
// double sqrt(double)
double sqrt(double n){
  JsVar *f = jsvNewFromFloat(n);
  JsVar *M = jspGetNamedVariable("Math");
  JsVar *sqrtt = jspGetNamedField(M,"sqrt",false);
  JsVar *res = jspeFunctionCall(sqrtt,0,0,false,1,&f);

  double d = jsvGetFloat(res);
  jsvUnLock(res);
  jsvUnLock(f);
  jsvUnLock(sqrtt);
  jsvUnLock(M);
  return d;
}
`);

/* This one might be what you mean? It looks like the 'double'
argument doesn't make it in correctly */
var c = E.compiledC(`
// double sqrt(JsVar, double)
double sqrt(JsVar *v, double n){
  JsVar *f = jsvNewFromFloat(n);
  JsVar *res = jspeFunctionCall(v,0,0,false,1,&f);
  double d = jsvGetFloat(res);
  jsvUnLock(res);
  jsvUnLock(f);
  return d;
}
`);
print(c.sqrt(x=>{print(x);return Math.sqrt(x)}, 20));

/* This one works - using just integers */
var c = E.compiledC(`
// int sqrt(JsVar, int)
int sqrt(JsVar *v, int n){
  JsVar *f = jsvNewFromInteger(n);
  JsVar *res = jspeFunctionCall(v,0,0,false,1,&f);
  int d = jsvGetInteger(res);
  jsvUnLock(res);
  jsvUnLock(f);
  return d;
}
`);
print(c.sqrt(x=>{print(x);return Math.sqrt(x)}, 16));

// This one works too, using the vars direct
var c = E.compiledC(`
// JsVar sqrt(JsVar, JsVar)
JsVar *sqrt(JsVar *v, JsVar *n){
  JsVar *res = jspeFunctionCall(v,0,0,false,1,&n);
  return res;
}
`);
`);
print(c.sqrt(x=>{print(x);return Math.sqrt(x)}, 16));
d3nd3 commented 6 months ago

Sorry for the above not ready to be tested code. I have prepared some code that runs this time.

I have a print() function inside the c, so you can see the value it returns. Its required because the double cannot be converted to anything, not even integer, missing functions.

But yes, I see that it works when integer is input, same as you. The issue arise when double is input into Math.sqrt via c.

Run the below code:

var c = E.compiledC(`
//double sqrt(void)
//int sqrtInt(void)

void reverse(char str[], int length) {
    int start = 0;
    int end = length - 1;
    while (start < end) {
        char temp = str[start];
        str[start] = str[end];
        str[end] = temp;
        start++;
        end--;
    }
}
void intToStr(int num, char str[]) {
    int i = 0;
    int isNegative = 0;

    // Handle zero separately
    if (num == 0) {
        str[i++] = '0';
    } else {
        // Handle negative numbers
        if (num < 0) {
            isNegative = 1;
            num = -num;
        }

        // Process individual digits
        while (num != 0) {
            int digit = num % 10;
            str[i++] = digit + '0';
            num = num / 10;
        }

        // Add negative sign if the number was negative
        if (isNegative) {
            str[i++] = '-';
        }

        // Reverse the string to get the correct order
        reverse(str, i);
    }

    // Add null terminator
    str[i] = '\0';
}
float doubleToFloat(double value) {
    union {
        double d;
        unsigned long long u64;
    } doubleUnion;

    union {
        float f;
        unsigned int u32;
    } floatUnion;

    doubleUnion.d = value;
    int sign = (doubleUnion.u64 >> 63) & 1;
    int exponent = (int)((doubleUnion.u64 >> 52) & 0x7FF);
    unsigned long long fraction = doubleUnion.u64 & 0xFFFFFFFFFFFFF;

    if (exponent == 0x7FF) {
        floatUnion.u32 = (sign << 31) | 0x7F800000 | ((fraction >> 29) & 0x007FFFFF);
    } else if (exponent == 0) {
        int shift = 1023 - 127;  // Double bias - Float bias
        unsigned int denormalized_exp = 1 - shift;
        fraction = fraction >> shift;
        floatUnion.u32 = (sign << 31) | (denormalized_exp << 23) | ((fraction >> 29) & 0x007FFFFF);
    } else {
        exponent -= 1023;  // Double bias
        if (exponent > 127) {
            exponent = 127;
        } else if (exponent < -126) {
            exponent = -126;
        }
        floatUnion.u32 = (sign << 31) | ((exponent + 127) << 23) | ((fraction >> 29) & 0x007FFFFF);
    }
    return floatUnion.f;
}

void print(short num) {
      JsVar *p = jspGetNamedVariable("print");
      char numS[16];
      intToStr(num,numS);
      JsVar *s = jsvNewFromString(numS);
      jsvUnLock(jspeFunctionCall(p,0,0,false,1,&s));

      jsvUnLock(s);
      jsvUnLock(p);
}
void printS(const char * input) {
      JsVar *p = jspGetNamedVariable("print");

      JsVar *s = jsvNewFromString(input);
      jsvUnLock(jspeFunctionCall(p,0,0,false,1,&s));

      jsvUnLock(s);
      jsvUnLock(p);
}

double sqrt(void) {
  printS("===SqrtDouble===");
  JsVar * G = jspGetNamedVariable("global");
  JsVar * M = jspGetNamedField(G,"Math",false);
  //JsVar * M = jspGetNamedVariable("Math");
  JsVar * sqrtt = jspGetNamedField(M,"sqrt",false);

  printS("c: input float: ");print(16);JsVar *f = jsvNewFromFloat(16.0);

  JsVar * res = jspeFunctionCall(sqrtt,0,0,false,1,&f);

  double d_out = jsvGetFloat(res);
  printS("c: output int: ");print(jsvGetInteger(res));
  printS("c: output double: ");print(doubleToFloat(d_out));

  jsvUnLock(res);
  jsvUnLock(sqrtt);
  jsvUnLock(M);
  jsvUnLock(G);

  return d_out;
}
int sqrtInt(void) {
  printS("===SqrtInt===");
  JsVar * G = jspGetNamedVariable("global");
  JsVar * M = jspGetNamedField(G,"Math",false);
  //JsVar * M = jspGetNamedVariable("Math");
  JsVar * sqrtt = jspGetNamedField(M,"sqrt",false);

  printS("c: input int: ");print(16);JsVar *f = jsvNewFromInteger(16);

  JsVar * res = jspeFunctionCall(sqrtt,0,0,false,1,&f);

  double d = jsvGetFloat(res);
  int i_out = jsvGetInteger(res);
  printS("c: output int: ");print(i_out);
  printS("c: output double: ");print(doubleToFloat(d));

  jsvUnLock(res);
  jsvUnLock(sqrtt);
  jsvUnLock(M);
  jsvUnLock(G);

  return i_out;
}
`);
print(`javascript: output ${c.sqrt()}`);
print(`javascript: output ${c.sqrtInt()}`);
gfwilliams commented 6 months ago

what device are you compiling for?

I just checked and it seems like a while back we switched nRF52-based builds to use a different ABI (softfp) but didn't update the compiler. That'd affect both calling and being called with double args. I've just tweaked it.

I get an error about __aeabi_f2iz when running your code now, but the double sqrt(JsVar, double) function I did above does seem to work now.

d3nd3 commented 6 months ago

what device are you compiling for?

I tested with Bangle.js 2.

gfwilliams commented 5 months ago

Closing this as I'm pretty sure the original issue is fixed after recent compiler changes