nota write decimal numbers via a string in the qjs_nota implementation, solving windows fp error
All checks were successful
Build and Deploy / build-linux (push) Successful in 1m11s
Build and Deploy / build-windows (CLANG64) (push) Successful in 10m2s
Build and Deploy / package-dist (push) Successful in 8s
Build and Deploy / deploy-gitea (push) Successful in 5s
Build and Deploy / deploy-itch (push) Successful in 9s

This commit is contained in:
2025-02-24 20:22:11 -06:00
parent 6c390aeae3
commit af0996f6ab
3 changed files with 180 additions and 5 deletions

View File

@@ -153,7 +153,6 @@ jobs:
deploy-itch: deploy-itch:
needs: [package-dist] needs: [package-dist]
if: ${{ false }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check Out Code - name: Check Out Code

View File

@@ -362,6 +362,177 @@ void nota_write_record(NotaBuffer *nb, unsigned long long count)
nb->size -= (10 - used); nb->size -= (10 - used);
} }
void nota_write_number_str(NotaBuffer *nb, const char *str)
{
/* -------------------------------------------
1) Parse sign
------------------------------------------- */
int negative = 0;
if (*str == '+') {
str++;
}
else if (*str == '-') {
negative = 1;
str++;
}
/* -------------------------------------------
2) Parse integer part
------------------------------------------- */
long long coefficient = 0;
int got_digits = 0;
while (*str >= '0' && *str <= '9') {
got_digits = 1;
int d = (*str - '0');
str++;
// Basic overflow check (very naive):
if (coefficient <= (LLONG_MAX - d) / 10) {
coefficient = coefficient * 10 + d;
} else {
// If you want to handle overflow by switching to float, do that here.
// For simplicity, let's just keep wrapping. In production, be careful!
coefficient = coefficient * 10 + d;
}
}
/* -------------------------------------------
3) Check for decimal part
------------------------------------------- */
int has_decimal_point = 0;
int fraction_digits = 0;
if (*str == '.') {
has_decimal_point = 1;
str++;
while (*str >= '0' && *str <= '9') {
got_digits = 1;
int d = (*str - '0');
str++;
fraction_digits++;
if (coefficient <= (LLONG_MAX - d) / 10) {
coefficient = coefficient * 10 + d;
} else {
// Same naive overflow comment
coefficient = coefficient * 10 + d;
}
}
}
/* -------------------------------------------
4) Check for exponent part
------------------------------------------- */
int exponent_negative = 0;
long long exponent_val = 0;
if (*str == 'e' || *str == 'E') {
str++;
if (*str == '+') {
str++;
}
else if (*str == '-') {
exponent_negative = 1;
str++;
}
while (*str >= '0' && *str <= '9') {
int d = (*str - '0');
str++;
if (exponent_val <= (LLONG_MAX - d) / 10) {
exponent_val = exponent_val * 10 + d;
} else {
// Again, naive overflow handling
exponent_val = exponent_val * 10 + d;
}
}
}
/* -------------------------------------------
5) If there were no valid digits at all,
store 0 and return. (simple fallback)
------------------------------------------- */
if (!got_digits) {
nota_write_int_buf(nb, 0);
return;
}
/* -------------------------------------------
6) Combine fraction digits into exponent
final_exponent = exponent_val - fraction_digits
(apply exponent sign if any)
------------------------------------------- */
if (exponent_negative) {
exponent_val = -exponent_val;
}
long long final_exponent = exponent_val - fraction_digits;
/* -------------------------------------------
7) Decide if we are storing an integer
or a float in Nota format.
-------------------------------------------
Rule used here:
- If there's no decimal point AND final_exponent == 0,
=> integer
- If we do have a decimal point, but fraction_digits == 0
and exponent_val == 0, then the user typed something
like "123." or "100.0". That is effectively an integer,
so store it as an integer if you want a purely numeric approach.
- Otherwise store as float.
------------------------------------------- */
// If "no decimal" => definitely integer:
// or decimal present but fraction_digits=0 & exponent_val=0 => integer
int treat_as_integer = 0;
if (!has_decimal_point && final_exponent == 0) {
treat_as_integer = 1;
}
else if (has_decimal_point && fraction_digits == 0 && exponent_val == 0) {
// Means "123." or "123.0"
treat_as_integer = 1;
}
if (treat_as_integer) {
// If negative => flip the sign in the stored value
if (negative) {
coefficient = -coefficient;
}
// Write the integer in Nota format (varint with sign bit)
nota_write_int_buf(nb, coefficient);
return;
}
/* -------------------------------------------
8) Write as float in Nota format
We do basically the same approach as
nota_write_float_buf does:
- NOTA_FLOAT nibble
- sign bit if negative
- exponent sign bit if final_exponent < 0
- varint of |final_exponent|
- varint of |coefficient|
------------------------------------------- */
{
char *p = nota_buffer_alloc(nb, 21); // Up to ~21 bytes worst-case
p[0] = NOTA_FLOAT;
if (negative) {
p[0] |= (1 << 3); // Mantissa sign bit
}
if (final_exponent < 0) {
p[0] |= (1 << 4); // Exponent sign bit
final_exponent = -final_exponent;
}
// Write exponent as varint (with 3 bits used in the first byte)
char *c = nota_continue_num(final_exponent, p, 3);
// Write the absolute coefficient (7 bits used in the first byte)
char *end = nota_continue_num(coefficient, c, 7);
// Adjust the buffer size to the actual used length
size_t used = (size_t)(end - p);
nb->size -= (21 - used);
}
}
void nota_write_number(NotaBuffer *nb, double n) void nota_write_number(NotaBuffer *nb, double n)
{ {
nota_write_int_or_float_buf(nb, n); nota_write_int_or_float_buf(nb, n);

View File

@@ -150,14 +150,19 @@ static void nota_encode_value(NotaEncodeContext *enc, JSValueConst val)
int tag = JS_VALUE_GET_TAG(val); int tag = JS_VALUE_GET_TAG(val);
switch (tag) { switch (tag) {
case JS_TAG_INT: case JS_TAG_INT: {
double d;
JS_ToFloat64(ctx, &d, val);
nota_write_number(&enc->nb, d);
return;
}
case JS_TAG_BIG_INT: case JS_TAG_BIG_INT:
case JS_TAG_FLOAT64: case JS_TAG_FLOAT64:
case JS_TAG_BIG_DECIMAL: case JS_TAG_BIG_DECIMAL:
case JS_TAG_BIG_FLOAT: { case JS_TAG_BIG_FLOAT: {
double d; const char *str = JS_ToCString(ctx, val);
JS_ToFloat64(ctx, &d, val); nota_write_number_str(&enc->nb, str);
nota_write_number(&enc->nb, d); JS_FreeCString(ctx, str);
return; return;
} }