#include "EXTERN.h"
#define PERL_IN_NUMERIC_C
#include "perl.h"
U32
Perl_cast_ulong(pTHX_ NV f)
{
if (f < 0.0)
return f < I32_MIN ? (U32) I32_MIN : (U32)(I32) f;
if (f < U32_MAX_P1) {
#if CASTFLAGS & 2
if (f < U32_MAX_P1_HALF)
return (U32) f;
f -= U32_MAX_P1_HALF;
return ((U32) f) | (1 + U32_MAX >> 1);
#else
return (U32) f;
#endif
}
return f > 0 ? U32_MAX : 0 ;
}
I32
Perl_cast_i32(pTHX_ NV f)
{
if (f < I32_MAX_P1)
return f < I32_MIN ? I32_MIN : (I32) f;
if (f < U32_MAX_P1) {
#if CASTFLAGS & 2
if (f < U32_MAX_P1_HALF)
return (I32)(U32) f;
f -= U32_MAX_P1_HALF;
return (I32)(((U32) f) | (1 + U32_MAX >> 1));
#else
return (I32)(U32) f;
#endif
}
return f > 0 ? (I32)U32_MAX : 0 ;
}
IV
Perl_cast_iv(pTHX_ NV f)
{
if (f < IV_MAX_P1)
return f < IV_MIN ? IV_MIN : (IV) f;
if (f < UV_MAX_P1) {
#if CASTFLAGS & 2
if (f < UV_MAX_P1_HALF)
return (IV)(UV) f;
f -= UV_MAX_P1_HALF;
return (IV)(((UV) f) | (1 + UV_MAX >> 1));
#else
return (IV)(UV) f;
#endif
}
return f > 0 ? (IV)UV_MAX : 0 ;
}
UV
Perl_cast_uv(pTHX_ NV f)
{
if (f < 0.0)
return f < IV_MIN ? (UV) IV_MIN : (UV)(IV) f;
if (f < UV_MAX_P1) {
#if CASTFLAGS & 2
if (f < UV_MAX_P1_HALF)
return (UV) f;
f -= UV_MAX_P1_HALF;
return ((UV) f) | (1 + UV_MAX >> 1);
#else
return (UV) f;
#endif
}
return f > 0 ? UV_MAX : 0 ;
}
#if defined(HUGE_VAL) || (defined(USE_LONG_DOUBLE) && defined(HUGE_VALL))
NV
Perl_huge(void)
{
# if defined(USE_LONG_DOUBLE) && defined(HUGE_VALL)
return HUGE_VALL;
# endif
return HUGE_VAL;
}
#endif
UV
Perl_grok_bin(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result) {
const char *s = start;
STRLEN len = *len_p;
UV value = 0;
NV value_nv = 0;
const UV max_div_2 = UV_MAX / 2;
bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES;
bool overflowed = FALSE;
if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) {
if (len >= 1) {
if (s[0] == 'b') {
s++;
len--;
}
else if (len >= 2 && s[0] == '0' && s[1] == 'b') {
s+=2;
len-=2;
}
}
}
for (; len-- && *s; s++) {
char bit = *s;
if (bit == '0' || bit == '1') {
redo:
if (!overflowed) {
if (value <= max_div_2) {
value = (value << 1) | (bit - '0');
continue;
}
if (ckWARN_d(WARN_OVERFLOW))
Perl_warner(aTHX_ packWARN(WARN_OVERFLOW),
"Integer overflow in binary number");
overflowed = TRUE;
value_nv = (NV) value;
}
value_nv *= 2.0;
value_nv += (NV)(bit - '0');
continue;
}
if (bit == '_' && len && allow_underscores && (bit = s[1])
&& (bit == '0' || bit == '1'))
{
--len;
++s;
goto redo;
}
if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT) && ckWARN(WARN_DIGIT))
Perl_warner(aTHX_ packWARN(WARN_DIGIT),
"Illegal binary digit '%c' ignored", *s);
break;
}
if ( ( overflowed && value_nv > 4294967295.0)
#if UVSIZE > 4
|| (!overflowed && value > 0xffffffff )
#endif
) {
if (ckWARN(WARN_PORTABLE))
Perl_warner(aTHX_ packWARN(WARN_PORTABLE),
"Binary number > 0b11111111111111111111111111111111 non-portable");
}
*len_p = s - start;
if (!overflowed) {
*flags = 0;
return value;
}
*flags = PERL_SCAN_GREATER_THAN_UV_MAX;
if (result)
*result = value_nv;
return UV_MAX;
}
UV
Perl_grok_hex(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result) {
const char *s = start;
STRLEN len = *len_p;
UV value = 0;
NV value_nv = 0;
const UV max_div_16 = UV_MAX / 16;
bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES;
bool overflowed = FALSE;
const char *hexdigit;
if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) {
if (len >= 1) {
if (s[0] == 'x') {
s++;
len--;
}
else if (len >= 2 && s[0] == '0' && s[1] == 'x') {
s+=2;
len-=2;
}
}
}
for (; len-- && *s; s++) {
hexdigit = strchr((char *) PL_hexdigit, *s);
if (hexdigit) {
redo:
if (!overflowed) {
if (value <= max_div_16) {
value = (value << 4) | ((hexdigit - PL_hexdigit) & 15);
continue;
}
if (ckWARN_d(WARN_OVERFLOW))
Perl_warner(aTHX_ packWARN(WARN_OVERFLOW),
"Integer overflow in hexadecimal number");
overflowed = TRUE;
value_nv = (NV) value;
}
value_nv *= 16.0;
value_nv += (NV)((hexdigit - PL_hexdigit) & 15);
continue;
}
if (*s == '_' && len && allow_underscores && s[1]
&& (hexdigit = strchr((char *) PL_hexdigit, s[1])))
{
--len;
++s;
goto redo;
}
if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT) && ckWARN(WARN_DIGIT))
Perl_warner(aTHX_ packWARN(WARN_DIGIT),
"Illegal hexadecimal digit '%c' ignored", *s);
break;
}
if ( ( overflowed && value_nv > 4294967295.0)
#if UVSIZE > 4
|| (!overflowed && value > 0xffffffff )
#endif
) {
if (ckWARN(WARN_PORTABLE))
Perl_warner(aTHX_ packWARN(WARN_PORTABLE),
"Hexadecimal number > 0xffffffff non-portable");
}
*len_p = s - start;
if (!overflowed) {
*flags = 0;
return value;
}
*flags = PERL_SCAN_GREATER_THAN_UV_MAX;
if (result)
*result = value_nv;
return UV_MAX;
}
UV
Perl_grok_oct(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result) {
const char *s = start;
STRLEN len = *len_p;
UV value = 0;
NV value_nv = 0;
const UV max_div_8 = UV_MAX / 8;
bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES;
bool overflowed = FALSE;
for (; len-- && *s; s++) {
int digit = *s - '0';
if (digit >= 0 && digit <= 7) {
redo:
if (!overflowed) {
if (value <= max_div_8) {
value = (value << 3) | digit;
continue;
}
if (ckWARN_d(WARN_OVERFLOW))
Perl_warner(aTHX_ packWARN(WARN_OVERFLOW),
"Integer overflow in octal number");
overflowed = TRUE;
value_nv = (NV) value;
}
value_nv *= 8.0;
value_nv += (NV)digit;
continue;
}
if (digit == ('_' - '0') && len && allow_underscores
&& (digit = s[1] - '0') && (digit >= 0 && digit <= 7))
{
--len;
++s;
goto redo;
}
if (digit == 8 || digit == 9) {
if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT) && ckWARN(WARN_DIGIT))
Perl_warner(aTHX_ packWARN(WARN_DIGIT),
"Illegal octal digit '%c' ignored", *s);
}
break;
}
if ( ( overflowed && value_nv > 4294967295.0)
#if UVSIZE > 4
|| (!overflowed && value > 0xffffffff )
#endif
) {
if (ckWARN(WARN_PORTABLE))
Perl_warner(aTHX_ packWARN(WARN_PORTABLE),
"Octal number > 037777777777 non-portable");
}
*len_p = s - start;
if (!overflowed) {
*flags = 0;
return value;
}
*flags = PERL_SCAN_GREATER_THAN_UV_MAX;
if (result)
*result = value_nv;
return UV_MAX;
}
NV
Perl_scan_bin(pTHX_ char *start, STRLEN len, STRLEN *retlen)
{
NV rnv;
I32 flags = *retlen ? PERL_SCAN_ALLOW_UNDERSCORES : 0;
UV ruv = grok_bin (start, &len, &flags, &rnv);
*retlen = len;
return (flags & PERL_SCAN_GREATER_THAN_UV_MAX) ? rnv : (NV)ruv;
}
NV
Perl_scan_oct(pTHX_ char *start, STRLEN len, STRLEN *retlen)
{
NV rnv;
I32 flags = *retlen ? PERL_SCAN_ALLOW_UNDERSCORES : 0;
UV ruv = grok_oct (start, &len, &flags, &rnv);
*retlen = len;
return (flags & PERL_SCAN_GREATER_THAN_UV_MAX) ? rnv : (NV)ruv;
}
NV
Perl_scan_hex(pTHX_ char *start, STRLEN len, STRLEN *retlen)
{
NV rnv;
I32 flags = *retlen ? PERL_SCAN_ALLOW_UNDERSCORES : 0;
UV ruv = grok_hex (start, &len, &flags, &rnv);
*retlen = len;
return (flags & PERL_SCAN_GREATER_THAN_UV_MAX) ? rnv : (NV)ruv;
}
bool
Perl_grok_numeric_radix(pTHX_ const char **sp, const char *send)
{
#ifdef USE_LOCALE_NUMERIC
if (PL_numeric_radix_sv && IN_LOCALE) {
STRLEN len;
char* radix = SvPV(PL_numeric_radix_sv, len);
if (*sp + len <= send && memEQ(*sp, radix, len)) {
*sp += len;
return TRUE;
}
}
#endif
if (*sp < send && **sp == '.') {
++*sp;
return TRUE;
}
return FALSE;
}
int
Perl_grok_number(pTHX_ const char *pv, STRLEN len, UV *valuep)
{
const char *s = pv;
const char *send = pv + len;
const UV max_div_10 = UV_MAX / 10;
const char max_mod_10 = UV_MAX % 10;
int numtype = 0;
int sawinf = 0;
int sawnan = 0;
while (s < send && isSPACE(*s))
s++;
if (s == send) {
return 0;
} else if (*s == '-') {
s++;
numtype = IS_NUMBER_NEG;
}
else if (*s == '+')
s++;
if (s == send)
return 0;
if (isDIGIT(*s)) {
UV value = *s - '0';
if (++s < send) {
int digit = *s - '0';
if (digit >= 0 && digit <= 9) {
value = value * 10 + digit;
if (++s < send) {
digit = *s - '0';
if (digit >= 0 && digit <= 9) {
value = value * 10 + digit;
if (++s < send) {
digit = *s - '0';
if (digit >= 0 && digit <= 9) {
value = value * 10 + digit;
if (++s < send) {
digit = *s - '0';
if (digit >= 0 && digit <= 9) {
value = value * 10 + digit;
if (++s < send) {
digit = *s - '0';
if (digit >= 0 && digit <= 9) {
value = value * 10 + digit;
if (++s < send) {
digit = *s - '0';
if (digit >= 0 && digit <= 9) {
value = value * 10 + digit;
if (++s < send) {
digit = *s - '0';
if (digit >= 0 && digit <= 9) {
value = value * 10 + digit;
if (++s < send) {
digit = *s - '0';
if (digit >= 0 && digit <= 9) {
value = value * 10 + digit;
if (++s < send) {
digit = *s - '0';
while (digit >= 0 && digit <= 9
&& (value < max_div_10
|| (value == max_div_10
&& digit <= max_mod_10))) {
value = value * 10 + digit;
if (++s < send)
digit = *s - '0';
else
break;
}
if (digit >= 0 && digit <= 9
&& (s < send)) {
do {
s++;
} while (s < send && isDIGIT(*s));
numtype |=
IS_NUMBER_GREATER_THAN_UV_MAX;
goto skip_value;
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
numtype |= IS_NUMBER_IN_UV;
if (valuep)
*valuep = value;
skip_value:
if (GROK_NUMERIC_RADIX(&s, send)) {
numtype |= IS_NUMBER_NOT_INT;
while (s < send && isDIGIT(*s))
s++;
}
}
else if (GROK_NUMERIC_RADIX(&s, send)) {
numtype |= IS_NUMBER_NOT_INT | IS_NUMBER_IN_UV;
if (s < send && isDIGIT(*s)) {
do {
s++;
} while (s < send && isDIGIT(*s));
if (valuep) {
*valuep = 0;
}
}
else
return 0;
} else if (*s == 'I' || *s == 'i') {
s++; if (s == send || (*s != 'N' && *s != 'n')) return 0;
s++; if (s == send || (*s != 'F' && *s != 'f')) return 0;
s++; if (s < send && (*s == 'I' || *s == 'i')) {
s++; if (s == send || (*s != 'N' && *s != 'n')) return 0;
s++; if (s == send || (*s != 'I' && *s != 'i')) return 0;
s++; if (s == send || (*s != 'T' && *s != 't')) return 0;
s++; if (s == send || (*s != 'Y' && *s != 'y')) return 0;
s++;
}
sawinf = 1;
} else if (*s == 'N' || *s == 'n') {
s++; if (s == send || (*s != 'A' && *s != 'a')) return 0;
s++; if (s == send || (*s != 'N' && *s != 'n')) return 0;
s++;
sawnan = 1;
} else
return 0;
if (sawinf) {
numtype &= IS_NUMBER_NEG;
numtype |= IS_NUMBER_INFINITY | IS_NUMBER_NOT_INT;
} else if (sawnan) {
numtype &= IS_NUMBER_NEG;
numtype |= IS_NUMBER_NAN | IS_NUMBER_NOT_INT;
} else if (s < send) {
if (*s == 'e' || *s == 'E') {
numtype &= IS_NUMBER_NEG;
numtype |= IS_NUMBER_NOT_INT;
s++;
if (s < send && (*s == '-' || *s == '+'))
s++;
if (s < send && isDIGIT(*s)) {
do {
s++;
} while (s < send && isDIGIT(*s));
}
else
return 0;
}
}
while (s < send && isSPACE(*s))
s++;
if (s >= send)
return numtype;
if (len == 10 && memEQ(pv, "0 but true", 10)) {
if (valuep)
*valuep = 0;
return IS_NUMBER_IN_UV;
}
return 0;
}
STATIC NV
S_mulexp10(NV value, I32 exponent)
{
NV result = 1.0;
NV power = 10.0;
bool negative = 0;
I32 bit;
if (exponent == 0)
return value;
if (value == 0)
return 0;
#if ((defined(VMS) && !defined(__IEEE_FP)) || defined(_UNICOS)) && defined(NV_MAX_10_EXP)
STMT_START {
NV exp_v = log10(value);
if (exponent >= NV_MAX_10_EXP || exponent + exp_v >= NV_MAX_10_EXP)
return NV_MAX;
if (exponent < 0) {
if (-(exponent + exp_v) >= NV_MAX_10_EXP)
return 0.0;
while (-exponent >= NV_MAX_10_EXP) {
value /= 10;
++exponent;
}
}
} STMT_END;
#endif
if (exponent < 0) {
negative = 1;
exponent = -exponent;
}
for (bit = 1; exponent; bit <<= 1) {
if (exponent & bit) {
exponent ^= bit;
result *= power;
if (exponent == 0) break;
}
power *= power;
}
return negative ? value / result : value * result;
}
NV
Perl_my_atof(pTHX_ const char* s)
{
NV x = 0.0;
#ifdef USE_LOCALE_NUMERIC
if (PL_numeric_local && IN_LOCALE) {
NV y;
Perl_atof2(s, x);
SET_NUMERIC_STANDARD();
Perl_atof2(s, y);
SET_NUMERIC_LOCAL();
if ((y < 0.0 && y < x) || (y > 0.0 && y > x))
return y;
}
else
Perl_atof2(s, x);
#else
Perl_atof2(s, x);
#endif
return x;
}
char*
Perl_my_atof2(pTHX_ const char* orig, NV* value)
{
NV result[3] = {0.0, 0.0, 0.0};
char* s = (char*)orig;
#ifdef USE_PERL_ATOF
UV accumulator[2] = {0,0};
bool negative = 0;
char* send = s + strlen(orig) - 1;
bool seen_digit = 0;
I32 exp_adjust[2] = {0,0};
I32 exp_acc[2] = {-1, -1};
I32 exponent = 0;
I32 seen_dp = 0;
I32 digit = 0;
I32 old_digit = 0;
I32 sig_digits = 0;
#define MAX_SIG_DIGITS (NV_DIG+2)
#define MAX_ACCUMULATE ( (UV) ((UV_MAX - 9)/10))
while (isSPACE(*s))
++s;
switch (*s) {
case '-':
negative = 1;
case '+':
++s;
}
while (1) {
if (isDIGIT(*s)) {
seen_digit = 1;
old_digit = digit;
digit = *s++ - '0';
if (seen_dp)
exp_adjust[1]++;
if (!sig_digits && digit == 0)
continue;
if (++sig_digits > MAX_SIG_DIGITS) {
if (digit > 5) {
++accumulator[seen_dp];
} else if (digit == 5) {
if (old_digit % 2) {
++accumulator[seen_dp];
}
}
if (seen_dp) {
exp_adjust[1]--;
} else {
exp_adjust[0]++;
}
while (isDIGIT(*s)) {
++s;
if (! seen_dp) {
exp_adjust[0]++;
}
}
}
else {
if (accumulator[seen_dp] > MAX_ACCUMULATE) {
result[seen_dp] = S_mulexp10(result[seen_dp],
exp_acc[seen_dp])
+ (NV)accumulator[seen_dp];
accumulator[seen_dp] = 0;
exp_acc[seen_dp] = 0;
}
accumulator[seen_dp] = accumulator[seen_dp] * 10 + digit;
++exp_acc[seen_dp];
}
}
else if (!seen_dp && GROK_NUMERIC_RADIX((const char **)&s, send)) {
seen_dp = 1;
if (sig_digits > MAX_SIG_DIGITS) {
++s;
while (isDIGIT(*s)) {
++s;
}
break;
}
}
else {
break;
}
}
result[0] = S_mulexp10(result[0], exp_acc[0]) + (NV)accumulator[0];
if (seen_dp) {
result[1] = S_mulexp10(result[1], exp_acc[1]) + (NV)accumulator[1];
}
if (seen_digit && (*s == 'e' || *s == 'E')) {
bool expnegative = 0;
++s;
switch (*s) {
case '-':
expnegative = 1;
case '+':
++s;
}
while (isDIGIT(*s))
exponent = exponent * 10 + (*s++ - '0');
if (expnegative)
exponent = -exponent;
}
if (seen_dp) {
result[2] = S_mulexp10(result[0],exponent+exp_adjust[0])
+ S_mulexp10(result[1],exponent-exp_adjust[1]);
} else {
result[2] = S_mulexp10(result[0],exponent+exp_adjust[0]);
}
if (negative)
result[2] = -result[2];
#endif
*value = result[2];
return s;
}
#if ! defined(HAS_MODFL) && defined(HAS_AINTL) && defined(HAS_COPYSIGNL)
long double
Perl_my_modfl(long double x, long double *ip)
{
*ip = aintl(x);
return (x == *ip ? copysignl(0.0L, x) : x - *ip);
}
#endif
#if ! defined(HAS_FREXPL) && defined(HAS_ILOGBL) && defined(HAS_SCALBNL)
long double
Perl_my_frexpl(long double x, int *e) {
*e = x == 0.0L ? 0 : ilogbl(x) + 1;
return (scalbnl(x, -*e));
}
#endif