mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
Implement printing in the specified radix for Number.prototype.toString().
JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai dbatyai.u-szeged@partner.samsung.com
This commit is contained in:
parent
c12914c71a
commit
5e0a355ab9
@ -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
|
||||
@ -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 */
|
||||
|
||||
/**
|
||||
|
||||
@ -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
|
||||
*
|
||||
@ -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
|
||||
*
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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. */
|
||||
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 */
|
||||
|
||||
|
||||
120
tests/jerry/number-prototype-to-string.js
Normal file
120
tests/jerry/number-prototype-to-string.js
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright 2015 Samsung Electronics Co., Ltd.
|
||||
// Copyright 2015 University of Szeged.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
assert((NaN).toString() === "NaN");
|
||||
assert((-Infinity).toString() === "-Infinity");
|
||||
assert((Infinity).toString() === "Infinity");
|
||||
assert((NaN).toString(6) === "NaN");
|
||||
assert((-Infinity).toString(7) === "-Infinity");
|
||||
assert((Infinity).toString(8) === "Infinity");
|
||||
assert((16).toString(16) === "10");
|
||||
assert((15).toString(16) === "f");
|
||||
assert((12.5).toString(4) === "30.2");
|
||||
assert((0.005).toString(4) === "0.000110132232011013223201101323");
|
||||
assert((2000).toString(4) === "133100");
|
||||
assert((2000).toString(3) === "2202002");
|
||||
assert((2000).toString(16) === "7d0");
|
||||
assert((0.03125).toString(2) === "0.00001");
|
||||
assert((0.03125).toString(16) === "0.08");
|
||||
assert((0.0001).toString(4) === "0.000000122031232023223013010030231")
|
||||
assert((0).toString(16) === "0");
|
||||
assert((-16).toString(16) === "-10");
|
||||
assert((-15).toString(16) === "-f");
|
||||
assert((-12.5).toString(4) === "-30.2");
|
||||
assert((-0.005).toString(4) === "-0.000110132232011013223201101323");
|
||||
assert((-2000).toString(4) === "-133100");
|
||||
assert((-2000).toString(3) === "-2202002");
|
||||
assert((-2000).toString(16) === "-7d0");
|
||||
assert((-0.03125).toString(2) === "-0.00001");
|
||||
assert((-0.03125).toString(16) === "-0.08");
|
||||
assert((-0.0001).toString(4) === "-0.000000122031232023223013010030231")
|
||||
assert((-0).toString(16) === "0");
|
||||
|
||||
assert((123400).toString(2) === "11110001000001000");
|
||||
assert((123400).toString(3) === "20021021101");
|
||||
assert((123400).toString(4) === "132020020");
|
||||
assert((123400).toString(5) === "12422100");
|
||||
assert((123400).toString(6) === "2351144");
|
||||
assert((123400).toString(7) === "1022524");
|
||||
assert((123400).toString(8) === "361010");
|
||||
assert((123400).toString(9) === "207241");
|
||||
assert((123400).toString(10) === "123400");
|
||||
assert((123400).toString(11) === "84792");
|
||||
assert((123400).toString(12) === "5b4b4");
|
||||
assert((123400).toString(13) === "44224");
|
||||
assert((123400).toString(14) === "32d84");
|
||||
assert((123400).toString(15) === "2686a");
|
||||
assert((123400).toString(16) === "1e208");
|
||||
assert((123400).toString(17) === "181ge");
|
||||
assert((123400).toString(18) === "132fa");
|
||||
assert((123400).toString(19) === "hife");
|
||||
assert((123400).toString(20) === "f8a0");
|
||||
assert((123400).toString(21) === "d6h4");
|
||||
assert((123400).toString(22) === "bcl2");
|
||||
assert((123400).toString(23) === "a365");
|
||||
assert((123400).toString(24) === "8m5g");
|
||||
assert((123400).toString(25) === "7mb0");
|
||||
assert((123400).toString(26) === "70e4");
|
||||
assert((123400).toString(27) === "677a");
|
||||
assert((123400).toString(28) === "5hb4");
|
||||
assert((123400).toString(29) === "51l5");
|
||||
assert((123400).toString(30) === "4h3a");
|
||||
assert((123400).toString(31) === "44ck");
|
||||
assert((123400).toString(32) === "3og8");
|
||||
assert((123400).toString(33) === "3ead");
|
||||
assert((123400).toString(34) === "34pe");
|
||||
assert((123400).toString(35) === "2upp");
|
||||
assert((123400).toString(36) === "2n7s");
|
||||
|
||||
assert((123400).toString(new Number(16)) === "1e208");
|
||||
|
||||
var digit_chars = ['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'];
|
||||
|
||||
for (radix = 2; radix <= 36; radix++) {
|
||||
for (num = 1; num < 100; num++) {
|
||||
var value = num;
|
||||
var str = "";
|
||||
while (value > 0) {
|
||||
str = digit_chars[value % radix] + str;
|
||||
value = Math.floor(value / radix);
|
||||
}
|
||||
|
||||
assert(str === (num).toString(radix));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
assert((123).toString(1));
|
||||
assert(false)
|
||||
} catch (e) {
|
||||
assert(e instanceof RangeError);
|
||||
}
|
||||
|
||||
try {
|
||||
assert((123).toString(37));
|
||||
assert(false)
|
||||
} catch (e) {
|
||||
assert(e instanceof RangeError);
|
||||
}
|
||||
|
||||
try {
|
||||
assert((123).toString(Infinity));
|
||||
assert(false)
|
||||
} catch (e) {
|
||||
assert(e instanceof RangeError);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user