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:
|
||||
needs: [package-dist]
|
||||
if: ${{ false }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
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_FLOAT64:
|
||||
case JS_TAG_BIG_DECIMAL:
|
||||
case JS_TAG_BIG_FLOAT: {
|
||||
double d;
|
||||
JS_ToFloat64(ctx, &d, val);
|
||||
nota_write_number(&enc->nb, d);
|
||||
const char *str = JS_ToCString(ctx, val);
|
||||
nota_write_number_str(&enc->nb, str);
|
||||
JS_FreeCString(ctx, str);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user