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
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:
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@@ -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
|
||||||
|
|||||||
171
source/nota.h
171
source/nota.h
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user