diff options
Diffstat (limited to 'src/literal.cc')
-rw-r--r-- | src/literal.cc | 834 |
1 files changed, 834 insertions, 0 deletions
diff --git a/src/literal.cc b/src/literal.cc new file mode 100644 index 00000000..2801b042 --- /dev/null +++ b/src/literal.cc @@ -0,0 +1,834 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * 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. + */ + +#include "literal.h" + +#include <assert.h> +#include <errno.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> + +#define HEX_DIGIT_BITS 4 + +/* The PLUS_ONE values are used because normal IEEE floats have an implicit + * leading one, so they have an additional bit of precision. */ + +#define F32_SIGN_SHIFT 31 +#define F32_SIG_BITS 23 +#define F32_SIG_MASK 0x7fffff +#define F32_SIG_PLUS_ONE_BITS 24 +#define F32_SIG_PLUS_ONE_MASK 0xffffff +#define F32_EXP_MASK 0xff +#define F32_MIN_EXP -127 +#define F32_MAX_EXP 128 +#define F32_EXP_BIAS 127 +#define F32_QUIET_NAN_TAG 0x400000 + +#define F64_SIGN_SHIFT 63 +#define F64_SIG_BITS 52 +#define F64_SIG_MASK 0xfffffffffffffULL +#define F64_SIG_PLUS_ONE_BITS 53 +#define F64_SIG_PLUS_ONE_MASK 0x1fffffffffffffULL +#define F64_EXP_MASK 0x7ff +#define F64_MIN_EXP -1023 +#define F64_MAX_EXP 1024 +#define F64_EXP_BIAS 1023 +#define F64_QUIET_NAN_TAG 0x8000000000000ULL + +static const char s_hex_digits[] = "0123456789abcdef"; + +WabtResult wabt_parse_hexdigit(char c, uint32_t* out) { + if ((unsigned int)(c - '0') <= 9) { + *out = c - '0'; + return WABT_OK; + } else if ((unsigned int)(c - 'a') <= 6) { + *out = 10 + (c - 'a'); + return WABT_OK; + } else if ((unsigned int)(c - 'A') <= 6) { + *out = 10 + (c - 'A'); + return WABT_OK; + } + return WABT_ERROR; +} + +/* return 1 if the non-NULL-terminated string starting with |start| and ending + with |end| starts with the NULL-terminated string |prefix|. */ +static bool string_starts_with(const char* start, + const char* end, + const char* prefix) { + while (start < end && *prefix) { + if (*start != *prefix) + return false; + start++; + prefix++; + } + return *prefix == 0; +} + +WabtResult wabt_parse_uint64(const char* s, const char* end, uint64_t* out) { + if (s == end) + return WABT_ERROR; + uint64_t value = 0; + if (*s == '0' && s + 1 < end && s[1] == 'x') { + s += 2; + if (s == end) + return WABT_ERROR; + for (; s < end; ++s) { + uint32_t digit; + if (WABT_FAILED(wabt_parse_hexdigit(*s, &digit))) + return WABT_ERROR; + uint64_t old_value = value; + value = value * 16 + digit; + /* check for overflow */ + if (old_value > value) + return WABT_ERROR; + } + } else { + for (; s < end; ++s) { + uint32_t digit = (*s - '0'); + if (digit > 9) + return WABT_ERROR; + uint64_t old_value = value; + value = value * 10 + digit; + /* check for overflow */ + if (old_value > value) + return WABT_ERROR; + } + } + if (s != end) + return WABT_ERROR; + *out = value; + return WABT_OK; +} + +WabtResult wabt_parse_int64(const char* s, + const char* end, + uint64_t* out, + WabtParseIntType parse_type) { + bool has_sign = false; + if (*s == '-' || *s == '+') { + if (parse_type == WABT_PARSE_UNSIGNED_ONLY) + return WABT_ERROR; + if (*s == '-') + has_sign = true; + s++; + } + uint64_t value = 0; + WabtResult result = wabt_parse_uint64(s, end, &value); + if (has_sign) { + if (value > (uint64_t)INT64_MAX + 1) /* abs(INT64_MIN) == INT64_MAX + 1 */ + return WABT_ERROR; + value = UINT64_MAX - value + 1; + } + *out = value; + return result; +} + +WabtResult wabt_parse_int32(const char* s, + const char* end, + uint32_t* out, + WabtParseIntType parse_type) { + uint64_t value; + bool has_sign = false; + if (*s == '-' || *s == '+') { + if (parse_type == WABT_PARSE_UNSIGNED_ONLY) + return WABT_ERROR; + if (*s == '-') + has_sign = true; + s++; + } + if (WABT_FAILED(wabt_parse_uint64(s, end, &value))) + return WABT_ERROR; + + if (has_sign) { + if (value > (uint64_t)INT32_MAX + 1) /* abs(INT32_MIN) == INT32_MAX + 1 */ + return WABT_ERROR; + value = UINT32_MAX - value + 1; + } else { + if (value > (uint64_t)UINT32_MAX) + return WABT_ERROR; + } + *out = (uint32_t)value; + return WABT_OK; +} + +/* floats */ +static uint32_t make_float(bool sign, int exp, uint32_t sig) { + assert(exp >= F32_MIN_EXP && exp <= F32_MAX_EXP); + assert(sig <= F32_SIG_MASK); + return ((uint32_t)sign << F32_SIGN_SHIFT) | + ((uint32_t)(exp + F32_EXP_BIAS) << F32_SIG_BITS) | sig; +} + +static uint32_t shift_float_and_round_to_nearest(uint32_t significand, + int shift) { + assert(shift > 0); + /* round ties to even */ + if (significand & ((uint32_t)1 << shift)) + significand += (uint32_t)1 << (shift - 1); + significand >>= shift; + return significand; +} + +static WabtResult parse_float_nan(const char* s, + const char* end, + uint32_t* out_bits) { + bool is_neg = false; + if (*s == '-') { + is_neg = true; + s++; + } else if (*s == '+') { + s++; + } + assert(string_starts_with(s, end, "nan")); + s += 3; + + uint32_t tag; + if (s != end) { + tag = 0; + assert(string_starts_with(s, end, ":0x")); + s += 3; + + for (; s < end; ++s) { + uint32_t digit; + if (WABT_FAILED(wabt_parse_hexdigit(*s, &digit))) + return WABT_ERROR; + tag = tag * 16 + digit; + /* check for overflow */ + if (tag > F32_SIG_MASK) + return WABT_ERROR; + } + + /* NaN cannot have a zero tag, that is reserved for infinity */ + if (tag == 0) + return WABT_ERROR; + } else { + tag = F32_QUIET_NAN_TAG; + } + + *out_bits = make_float(is_neg, F32_MAX_EXP, tag); + return WABT_OK; +} + +static void parse_float_hex(const char* s, + const char* end, + uint32_t* out_bits) { + bool is_neg = false; + if (*s == '-') { + is_neg = true; + s++; + } else if (*s == '+') { + s++; + } + assert(string_starts_with(s, end, "0x")); + s += 2; + + /* loop over the significand; everything up to the 'p'. + this code is a bit nasty because we want to support extra zeroes anywhere + without having to use many significand bits. + e.g. + 0x00000001.0p0 => significand = 1, significand_exponent = 0 + 0x10000000.0p0 => significand = 1, significand_exponent = 28 + 0x0.000001p0 => significand = 1, significand_exponent = -24 + */ + bool seen_dot = false; + uint32_t significand = 0; + /* how much to shift |significand| if a non-zero value is appended */ + int significand_shift = 0; + int significand_bits = 0; /* bits of |significand| */ + int significand_exponent = 0; /* exponent adjustment due to dot placement */ + for (; s < end; ++s) { + uint32_t digit; + if (*s == '.') { + if (significand != 0) + significand_exponent += significand_shift; + significand_shift = 0; + seen_dot = true; + continue; + } else if (WABT_FAILED(wabt_parse_hexdigit(*s, &digit))) { + break; + } + significand_shift += HEX_DIGIT_BITS; + if (digit != 0 && (significand == 0 || + significand_bits + significand_shift <= + F32_SIG_BITS + 1 + HEX_DIGIT_BITS)) { + if (significand != 0) + significand <<= significand_shift; + if (seen_dot) + significand_exponent -= significand_shift; + significand += digit; + significand_shift = 0; + significand_bits += HEX_DIGIT_BITS; + } + } + + if (!seen_dot) + significand_exponent += significand_shift; + + assert(s < end && *s == 'p'); + s++; + + if (significand == 0) { + /* 0 or -0 */ + *out_bits = make_float(is_neg, F32_MIN_EXP, 0); + return; + } + + assert(s < end); + int exponent = 0; + bool exponent_is_neg = false; + /* exponent is always positive, but significand_exponent is signed. + significand_exponent_add is negated if exponent will be negative, so it can + be easily summed to see if the exponent is too large (see below) */ + int significand_exponent_add = 0; + if (*s == '-') { + exponent_is_neg = true; + significand_exponent_add = -significand_exponent; + s++; + } else if (*s == '+') { + s++; + significand_exponent_add = significand_exponent; + } + + for (; s < end; ++s) { + uint32_t digit = (*s - '0'); + assert(digit <= 9); + exponent = exponent * 10 + digit; + if (exponent + significand_exponent_add >= F32_MAX_EXP) + break; + } + + if (exponent_is_neg) + exponent = -exponent; + + significand_bits = sizeof(uint32_t) * 8 - wabt_clz_u32(significand); + /* -1 for the implicit 1 bit of the significand */ + exponent += significand_exponent + significand_bits - 1; + + if (exponent >= F32_MAX_EXP) { + /* inf or -inf */ + *out_bits = make_float(is_neg, F32_MAX_EXP, 0); + } else if (exponent <= F32_MIN_EXP) { + /* maybe subnormal */ + if (significand_bits > F32_SIG_BITS) { + significand = shift_float_and_round_to_nearest( + significand, significand_bits - F32_SIG_BITS); + } else if (significand_bits < F32_SIG_BITS) { + significand <<= (F32_SIG_BITS - significand_bits); + } + + int shift = F32_MIN_EXP - exponent; + if (shift < F32_SIG_BITS) { + if (shift) { + significand = + shift_float_and_round_to_nearest(significand, shift) & F32_SIG_MASK; + } + exponent = F32_MIN_EXP; + + if (significand != 0) { + *out_bits = make_float(is_neg, exponent, significand); + return; + } + } + + /* not subnormal, too small; return 0 or -0 */ + *out_bits = make_float(is_neg, F32_MIN_EXP, 0); + } else { + /* normal value */ + if (significand_bits > F32_SIG_PLUS_ONE_BITS) { + significand = shift_float_and_round_to_nearest( + significand, significand_bits - F32_SIG_PLUS_ONE_BITS); + if (significand > F32_SIG_PLUS_ONE_MASK) + exponent++; + } else if (significand_bits < F32_SIG_PLUS_ONE_BITS) { + significand <<= (F32_SIG_PLUS_ONE_BITS - significand_bits); + } + + *out_bits = make_float(is_neg, exponent, significand & F32_SIG_MASK); + } +} + +static void parse_float_infinity(const char* s, + const char* end, + uint32_t* out_bits) { + bool is_neg = false; + if (*s == '-') { + is_neg = true; + s++; + } else if (*s == '+') { + s++; + } + assert(string_starts_with(s, end, "infinity")); + *out_bits = make_float(is_neg, F32_MAX_EXP, 0); +} + +WabtResult wabt_parse_float(WabtLiteralType literal_type, + const char* s, + const char* end, + uint32_t* out_bits) { + switch (literal_type) { + case WABT_LITERAL_TYPE_INT: + case WABT_LITERAL_TYPE_FLOAT: { + errno = 0; + char* endptr; + float value; + value = strtof(s, &endptr); + if (endptr != end || + ((value == 0 || value == HUGE_VALF || value == -HUGE_VALF) && + errno != 0)) + return WABT_ERROR; + + memcpy(out_bits, &value, sizeof(value)); + return WABT_OK; + } + + case WABT_LITERAL_TYPE_HEXFLOAT: + parse_float_hex(s, end, out_bits); + return WABT_OK; + + case WABT_LITERAL_TYPE_INFINITY: + parse_float_infinity(s, end, out_bits); + return WABT_OK; + + case WABT_LITERAL_TYPE_NAN: + return parse_float_nan(s, end, out_bits); + + default: + assert(0); + return WABT_ERROR; + } +} + +void wabt_write_float_hex(char* out, size_t size, uint32_t bits) { + /* 1234567890123456 */ + /* -0x#.######p-### */ + /* -nan:0x###### */ + /* -infinity */ + char buffer[WABT_MAX_FLOAT_HEX]; + char* p = buffer; + bool is_neg = (bits >> F32_SIGN_SHIFT); + int exp = ((bits >> F32_SIG_BITS) & F32_EXP_MASK) - F32_EXP_BIAS; + uint32_t sig = bits & F32_SIG_MASK; + + if (is_neg) + *p++ = '-'; + if (exp == F32_MAX_EXP) { + /* infinity or nan */ + if (sig == 0) { + strcpy(p, "infinity"); + p += 8; + } else { + strcpy(p, "nan"); + p += 3; + if (sig != F32_QUIET_NAN_TAG) { + strcpy(p, ":0x"); + p += 3; + /* skip leading zeroes */ + int num_nybbles = sizeof(uint32_t) * 8 / 4; + while ((sig & 0xf0000000) == 0) { + sig <<= 4; + num_nybbles--; + } + while (num_nybbles) { + uint32_t nybble = (sig >> (sizeof(uint32_t) * 8 - 4)) & 0xf; + *p++ = s_hex_digits[nybble]; + sig <<= 4; + --num_nybbles; + } + } + } + } else { + bool is_zero = sig == 0 && exp == F32_MIN_EXP; + strcpy(p, "0x"); + p += 2; + *p++ = is_zero ? '0' : '1'; + + /* shift sig up so the top 4-bits are at the top of the uint32 */ + sig <<= sizeof(uint32_t) * 8 - F32_SIG_BITS; + + if (sig) { + if (exp == F32_MIN_EXP) { + /* subnormal; shift the significand up, and shift out the implicit 1 */ + uint32_t leading_zeroes = wabt_clz_u32(sig); + if (leading_zeroes < 31) + sig <<= leading_zeroes + 1; + else + sig = 0; + exp -= leading_zeroes; + } + + *p++ = '.'; + while (sig) { + uint32_t nybble = (sig >> (sizeof(uint32_t) * 8 - 4)) & 0xf; + *p++ = s_hex_digits[nybble]; + sig <<= 4; + } + } + *p++ = 'p'; + if (is_zero) { + strcpy(p, "+0"); + p += 2; + } else { + if (exp < 0) { + *p++ = '-'; + exp = -exp; + } else { + *p++ = '+'; + } + if (exp >= 100) *p++ = '1'; + if (exp >= 10) *p++ = '0' + (exp / 10) % 10; + *p++ = '0' + exp % 10; + } + } + + size_t len = p - buffer; + if (len >= size) + len = size - 1; + memcpy(out, buffer, len); + out[len] = '\0'; +} + +/* doubles */ +static uint64_t make_double(bool sign, int exp, uint64_t sig) { + assert(exp >= F64_MIN_EXP && exp <= F64_MAX_EXP); + assert(sig <= F64_SIG_MASK); + return ((uint64_t)sign << F64_SIGN_SHIFT) | + ((uint64_t)(exp + F64_EXP_BIAS) << F64_SIG_BITS) | sig; +} + +static uint64_t shift_double_and_round_to_nearest(uint64_t significand, + int shift) { + assert(shift > 0); + /* round ties to even */ + if (significand & ((uint64_t)1 << shift)) + significand += (uint64_t)1 << (shift - 1); + significand >>= shift; + return significand; +} + +static WabtResult parse_double_nan(const char* s, + const char* end, + uint64_t* out_bits) { + bool is_neg = false; + if (*s == '-') { + is_neg = true; + s++; + } else if (*s == '+') { + s++; + } + assert(string_starts_with(s, end, "nan")); + s += 3; + + uint64_t tag; + if (s != end) { + tag = 0; + if (!string_starts_with(s, end, ":0x")) + return WABT_ERROR; + s += 3; + + for (; s < end; ++s) { + uint32_t digit; + if (WABT_FAILED(wabt_parse_hexdigit(*s, &digit))) + return WABT_ERROR; + tag = tag * 16 + digit; + /* check for overflow */ + if (tag > F64_SIG_MASK) + return WABT_ERROR; + } + + /* NaN cannot have a zero tag, that is reserved for infinity */ + if (tag == 0) + return WABT_ERROR; + } else { + tag = F64_QUIET_NAN_TAG; + } + + *out_bits = make_double(is_neg, F64_MAX_EXP, tag); + return WABT_OK; +} + +static void parse_double_hex(const char* s, + const char* end, + uint64_t* out_bits) { + bool is_neg = false; + if (*s == '-') { + is_neg = true; + s++; + } else if (*s == '+') { + s++; + } + assert(string_starts_with(s, end, "0x")); + s += 2; + + /* see the similar comment in parse_float_hex */ + bool seen_dot = false; + uint64_t significand = 0; + /* how much to shift |significand| if a non-zero value is appended */ + int significand_shift = 0; + int significand_bits = 0; /* bits of |significand| */ + int significand_exponent = 0; /* exponent adjustment due to dot placement */ + for (; s < end; ++s) { + uint32_t digit; + if (*s == '.') { + if (significand != 0) + significand_exponent += significand_shift; + significand_shift = 0; + seen_dot = true; + continue; + } else if (WABT_FAILED(wabt_parse_hexdigit(*s, &digit))) { + break; + } + significand_shift += HEX_DIGIT_BITS; + if (digit != 0 && (significand == 0 || + significand_bits + significand_shift <= + F64_SIG_BITS + 1 + HEX_DIGIT_BITS)) { + if (significand != 0) + significand <<= significand_shift; + if (seen_dot) + significand_exponent -= significand_shift; + significand += digit; + significand_shift = 0; + significand_bits += HEX_DIGIT_BITS; + } + } + + if (!seen_dot) + significand_exponent += significand_shift; + + assert(s < end && *s == 'p'); + s++; + + if (significand == 0) { + /* 0 or -0 */ + *out_bits = make_double(is_neg, F64_MIN_EXP, 0); + return; + } + + assert(s < end); + int exponent = 0; + bool exponent_is_neg = false; + /* exponent is always positive, but significand_exponent is signed. + significand_exponent_add is negated if exponent will be negative, so it can + be easily summed to see if the exponent is too large (see below) */ + int significand_exponent_add = 0; + if (*s == '-') { + exponent_is_neg = true; + significand_exponent_add = -significand_exponent; + s++; + } else if (*s == '+') { + s++; + significand_exponent_add = significand_exponent; + } + + for (; s < end; ++s) { + uint32_t digit = (*s - '0'); + assert(digit <= 9); + exponent = exponent * 10 + digit; + if (exponent + significand_exponent_add >= F64_MAX_EXP) + break; + } + + if (exponent_is_neg) + exponent = -exponent; + + significand_bits = sizeof(uint64_t) * 8 - wabt_clz_u64(significand); + /* -1 for the implicit 1 bit of the significand */ + exponent += significand_exponent + significand_bits - 1; + + if (exponent >= F64_MAX_EXP) { + /* inf or -inf */ + *out_bits = make_double(is_neg, F64_MAX_EXP, 0); + } else if (exponent <= F64_MIN_EXP) { + /* maybe subnormal */ + if (significand_bits > F64_SIG_BITS) { + significand = shift_double_and_round_to_nearest( + significand, significand_bits - F64_SIG_BITS); + } else if (significand_bits < F64_SIG_BITS) { + significand <<= (F64_SIG_BITS - significand_bits); + } + + int shift = F64_MIN_EXP - exponent; + if (shift < F64_SIG_BITS) { + if (shift) { + significand = shift_double_and_round_to_nearest(significand, shift) & + F64_SIG_MASK; + } + exponent = F64_MIN_EXP; + + if (significand != 0) { + *out_bits = make_double(is_neg, exponent, significand); + return; + } + } + + /* not subnormal, too small; return 0 or -0 */ + *out_bits = make_double(is_neg, F64_MIN_EXP, 0); + } else { + /* normal value */ + if (significand_bits > F64_SIG_PLUS_ONE_BITS) { + significand = shift_double_and_round_to_nearest( + significand, significand_bits - F64_SIG_PLUS_ONE_BITS); + if (significand > F64_SIG_PLUS_ONE_MASK) + exponent++; + } else if (significand_bits < F64_SIG_PLUS_ONE_BITS) { + significand <<= (F64_SIG_PLUS_ONE_BITS - significand_bits); + } + + *out_bits = make_double(is_neg, exponent, significand & F64_SIG_MASK); + } +} + +static void parse_double_infinity(const char* s, + const char* end, + uint64_t* out_bits) { + bool is_neg = false; + if (*s == '-') { + is_neg = true; + s++; + } else if (*s == '+') { + s++; + } + assert(string_starts_with(s, end, "infinity")); + *out_bits = make_double(is_neg, F64_MAX_EXP, 0); +} + +WabtResult wabt_parse_double(WabtLiteralType literal_type, + const char* s, + const char* end, + uint64_t* out_bits) { + switch (literal_type) { + case WABT_LITERAL_TYPE_INT: + case WABT_LITERAL_TYPE_FLOAT: { + errno = 0; + char* endptr; + double value; + value = strtod(s, &endptr); + if (endptr != end || + ((value == 0 || value == HUGE_VAL || value == -HUGE_VAL) && + errno != 0)) + return WABT_ERROR; + + memcpy(out_bits, &value, sizeof(value)); + return WABT_OK; + } + + case WABT_LITERAL_TYPE_HEXFLOAT: + parse_double_hex(s, end, out_bits); + return WABT_OK; + + case WABT_LITERAL_TYPE_INFINITY: + parse_double_infinity(s, end, out_bits); + return WABT_OK; + + case WABT_LITERAL_TYPE_NAN: + return parse_double_nan(s, end, out_bits); + + default: + assert(0); + return WABT_ERROR; + } +} + +void wabt_write_double_hex(char* out, size_t size, uint64_t bits) { + /* 123456789012345678901234 */ + /* -0x#.#############p-#### */ + /* -nan:0x############# */ + /* -infinity */ + char buffer[WABT_MAX_DOUBLE_HEX]; + char* p = buffer; + bool is_neg = (bits >> F64_SIGN_SHIFT); + int exp = ((bits >> F64_SIG_BITS) & F64_EXP_MASK) - F64_EXP_BIAS; + uint64_t sig = bits & F64_SIG_MASK; + + if (is_neg) + *p++ = '-'; + if (exp == F64_MAX_EXP) { + /* infinity or nan */ + if (sig == 0) { + strcpy(p, "infinity"); + p += 8; + } else { + strcpy(p, "nan"); + p += 3; + if (sig != F64_QUIET_NAN_TAG) { + strcpy(p, ":0x"); + p += 3; + /* skip leading zeroes */ + int num_nybbles = sizeof(uint64_t) * 8 / 4; + while ((sig & 0xf000000000000000ULL) == 0) { + sig <<= 4; + num_nybbles--; + } + while (num_nybbles) { + uint32_t nybble = (sig >> (sizeof(uint64_t) * 8 - 4)) & 0xf; + *p++ = s_hex_digits[nybble]; + sig <<= 4; + --num_nybbles; + } + } + } + } else { + bool is_zero = sig == 0 && exp == F64_MIN_EXP; + strcpy(p, "0x"); + p += 2; + *p++ = is_zero ? '0' : '1'; + + /* shift sig up so the top 4-bits are at the top of the uint32 */ + sig <<= sizeof(uint64_t) * 8 - F64_SIG_BITS; + + if (sig) { + if (exp == F64_MIN_EXP) { + /* subnormal; shift the significand up, and shift out the implicit 1 */ + uint32_t leading_zeroes = wabt_clz_u64(sig); + if (leading_zeroes < 63) + sig <<= leading_zeroes + 1; + else + sig = 0; + exp -= leading_zeroes; + } + + *p++ = '.'; + while (sig) { + uint32_t nybble = (sig >> (sizeof(uint64_t) * 8 - 4)) & 0xf; + *p++ = s_hex_digits[nybble]; + sig <<= 4; + } + } + *p++ = 'p'; + if (is_zero) { + strcpy(p, "+0"); + p += 2; + } else { + if (exp < 0) { + *p++ = '-'; + exp = -exp; + } else { + *p++ = '+'; + } + if (exp >= 1000) *p++ = '1'; + if (exp >= 100) *p++ = '0' + (exp / 100) % 10; + if (exp >= 10) *p++ = '0' + (exp / 10) % 10; + *p++ = '0' + exp % 10; + } + } + + size_t len = p - buffer; + if (len >= size) + len = size - 1; + memcpy(out, buffer, len); + out[len] = '\0'; +} |