Skip to content

Implement printing in the specified radix for Number.prototype.toString(). #306

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions jerry-core/ecma/base/ecma-globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,30 @@ typedef float ecma_number_t;
* Maximum number of significant digits that ecma-number can store
*/
#define ECMA_NUMBER_MAX_DIGITS (9)

/**
* Width of sign field
*
* See also:
* IEEE-754 2008, 3.6, Table 3.5
*/
#define ECMA_NUMBER_SIGN_WIDTH (1)

/**
* Width of biased exponent field
*
* See also:
* IEEE-754 2008, 3.6, Table 3.5
*/
#define ECMA_NUMBER_BIASED_EXP_WIDTH (8)

/**
* Width of fraction field
*
* See also:
* IEEE-754 2008, 3.6, Table 3.5
*/
#define ECMA_NUMBER_FRACTION_WIDTH (23)
#elif CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64
/**
* Description of an ecma-number
Expand All @@ -590,6 +614,30 @@ typedef double ecma_number_t;
* Maximum number of significant digits that ecma-number can store
*/
#define ECMA_NUMBER_MAX_DIGITS (18)

/**
* Width of sign field
*
* See also:
* IEEE-754 2008, 3.6, Table 3.5
*/
#define ECMA_NUMBER_SIGN_WIDTH (1)

/**
* Width of biased exponent field
*
* See also:
* IEEE-754 2008, 3.6, Table 3.5
*/
#define ECMA_NUMBER_BIASED_EXP_WIDTH (11)

/**
* Width of fraction field
*
* See also:
* IEEE-754 2008, 3.6, Table 3.5
*/
#define ECMA_NUMBER_FRACTION_WIDTH (52)
#endif /* CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64 */

/**
Expand Down
48 changes: 0 additions & 48 deletions jerry-core/ecma/base/ecma-helpers-number.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,6 @@
#if CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT32
JERRY_STATIC_ASSERT (sizeof (ecma_number_t) == sizeof (uint32_t));

/**
* Width of sign field
*
* See also:
* IEEE-754 2008, 3.6, Table 3.5
*/
#define ECMA_NUMBER_SIGN_WIDTH (1)

/**
* Width of biased exponent field
*
* See also:
* IEEE-754 2008, 3.6, Table 3.5
*/
#define ECMA_NUMBER_BIASED_EXP_WIDTH (8)

/**
* Width of fraction field
*
* See also:
* IEEE-754 2008, 3.6, Table 3.5
*/
#define ECMA_NUMBER_FRACTION_WIDTH (23)

/**
* Packing sign, fraction and biased exponent to ecma-number
*
Expand Down Expand Up @@ -137,30 +113,6 @@ const ecma_number_t ecma_number_relative_eps = 1.0e-10f;
#elif CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64
JERRY_STATIC_ASSERT (sizeof (ecma_number_t) == sizeof (uint64_t));

/**
* Width of sign field
*
* See also:
* IEEE-754 2008, 3.6, Table 3.5
*/
#define ECMA_NUMBER_SIGN_WIDTH (1)

/**
* Width of biased exponent field
*
* See also:
* IEEE-754 2008, 3.6, Table 3.5
*/
#define ECMA_NUMBER_BIASED_EXP_WIDTH (11)

/**
* Width of fraction field
*
* See also:
* IEEE-754 2008, 3.6, Table 3.5
*/
#define ECMA_NUMBER_FRACTION_WIDTH (52)

/**
* Packing sign, fraction and biased exponent to ecma-number
*
Expand Down
212 changes: 210 additions & 2 deletions jerry-core/ecma/builtin-objects/ecma-builtin-number-prototype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
#include "ecma-objects.h"
#include "ecma-string-object.h"
#include "ecma-try-catch-macro.h"
#include "fdlibm-math.h"
#include "jrt.h"
#include "jrt-libc-includes.h"

#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_NUMBER_BUILTIN

Expand Down Expand Up @@ -118,14 +120,220 @@ ecma_builtin_number_prototype_object_to_string (ecma_value_t this_arg, /**< this
return ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE));
}

if (arguments_list_len == 0)
if (arguments_list_len == 0
|| ecma_number_is_nan (this_arg_number)
|| ecma_number_is_infinity (this_arg_number)
|| ecma_number_is_zero (this_arg_number))
{
ecma_string_t *ret_str_p = ecma_new_ecma_string_from_number (this_arg_number);

return ecma_make_normal_completion_value (ecma_make_string_value (ret_str_p));
}
else
{
ECMA_BUILTIN_CP_UNIMPLEMENTED (arguments_list_p);
const lit_utf8_byte_t digit_chars[36] =
{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z'
};

ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
ECMA_OP_TO_NUMBER_TRY_CATCH (arg_num, arguments_list_p[0], ret_value);

uint32_t radix = ecma_number_to_uint32 (arg_num);

if (radix < 2 || radix > 36)
{
ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_RANGE));
}
else if (radix == 10)
{
ecma_string_t *ret_str_p = ecma_new_ecma_string_from_number (this_arg_number);

ret_value = ecma_make_normal_completion_value (ecma_make_string_value (ret_str_p));
}
else
{
uint64_t digits;
int32_t num_digits;
int32_t exponent;
bool is_negative = false;

if (ecma_number_is_negative (this_arg_number))
{
this_arg_number = -this_arg_number;
is_negative = true;
}

ecma_number_to_decimal (this_arg_number, &digits, &num_digits, &exponent);

exponent = exponent - num_digits;
bool is_scale_negative = false;

/* Calculate the scale of the number in the specified radix. */
int scale = (int) -floor ((log (10) / log (radix)) * exponent);

int buff_size;

if (is_scale_negative)
{
buff_size = (int) floor ((log (this_arg_number) / log (10))) + 1;
}
else
{
buff_size = scale + ECMA_NUMBER_FRACTION_WIDTH + 2;
}

if (is_negative)
{
buff_size++;
}

if (scale < 0)
{
is_scale_negative = true;
scale = -scale;
}

/* Normalize the number, so that it is as close to 0 exponent as possible. */
for (int i = 0; i < scale; i++)
{
if (is_scale_negative)
{
this_arg_number /= (ecma_number_t) radix;
}
else
{
this_arg_number *= (ecma_number_t) radix;
}
}

uint64_t whole = (uint64_t) this_arg_number;
ecma_number_t fraction = this_arg_number - (ecma_number_t) whole;

MEM_DEFINE_LOCAL_ARRAY (buff, buff_size, lit_utf8_byte_t);
int buff_index = 0;

/* Calculate digits for whole part. */
while (whole > 0)
{
buff[buff_index++] = (lit_utf8_byte_t) (whole % radix);
whole /= radix;
}

/* Calculate where we have to put the radix point. */
int point = is_scale_negative ? buff_index + scale : buff_index - scale;

/* Reverse the digits, since they are backwards. */
for (int i = 0; i < buff_index / 2; i++)
{
lit_utf8_byte_t swap = buff[i];
buff[i] = buff[buff_index - i - 1];
buff[buff_index - i - 1] = swap;
}

bool should_round = false;
/* Calculate digits for fractional part. */
for (int iter_count = 0;
iter_count < ECMA_NUMBER_FRACTION_WIDTH && (fraction != 0 || is_scale_negative);
iter_count++)
{
fraction *= (ecma_number_t) radix;
lit_utf8_byte_t digit = (lit_utf8_byte_t) floor (fraction);

buff[buff_index++] = digit;
fraction -= (ecma_number_t) floor (fraction);

if (iter_count == scale && is_scale_negative)
{
/*
* When scale is negative, that means the original number did not have a fractional part,
* but by normalizing it, we introduced one. In this case, when the iteration count reaches
* the scale, we already have the number, but it may be incorrect, so we calculate
* one extra digit that we round off just to make sure.
*/
should_round = true;
break;
}
}

if (should_round)
{
/* Round off last digit. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: of

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's not :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, right :)

if (buff[buff_index - 1] > radix / 2)
{
buff[buff_index - 2]++;
}

buff_index--;

/* Propagate carry. */
for (int i = buff_index - 1; i > 0 && buff[i] >= radix; i--)
{
buff[i] = (lit_utf8_byte_t) (buff[i] - radix);
buff[i - 1]++;
}

/* Carry propagated over the whole number, need to add a leading digit. */
if (buff[0] >= radix)
{
memmove (buff + 1, buff, (size_t) buff_index);
buff_index++;
buff[0] = 1;
}
}

/* Remove trailing zeros from fraction. */
while (buff_index - 1 > point && buff[buff_index - 1] == 0)
{
buff_index--;
}

/* Add leading zeros in case place of radix point is negative. */
if (point <= 0)
{
memmove (buff - point + 1, buff, (size_t) buff_index);
buff_index += -point + 1;

for (int i = 0; i < -point + 1; i++)
{
buff[i] = 0;
}

point = 1;
}

/* Convert digits to characters. */
for (int i = 0; i < buff_index; i++)
{
buff[i] = digit_chars[buff[i]];
}

/* Place radix point to the required position. */
if (point < buff_index)
{
memmove (buff + point + 1, buff + point, (size_t) buff_index);
buff[point] = '.';
buff_index++;
}

/* Add negative sign if necessary. */
if (is_negative)
{
memmove (buff + 1, buff, (size_t) buff_index);
buff_index++;
buff[0] = '-';
}

JERRY_ASSERT (buff_index <= buff_size);
ecma_string_t* str_p = ecma_new_ecma_string_from_utf8 (buff, (lit_utf8_size_t) buff_index);
ret_value = ecma_make_normal_completion_value (ecma_make_string_value (str_p));
MEM_FINALIZE_LOCAL_ARRAY (buff);
}
ECMA_OP_TO_NUMBER_FINALIZE (arg_num);
return ret_value;
}
} /* ecma_builtin_number_prototype_object_to_string */

Expand Down
Loading