/* -*- mode: C++; c-file-style: "bsd" -*- */ #include "exttypes.h" #include SV * porbit_ll_from_longlong (long long val) { SV *rv; SV *result = newSV(0); SvUPGRADE (result, SVt_NV); LL_VALUE(result) = val; rv = newRV_noinc (result); sv_bless (rv, gv_stashpv ("CORBA::LongLong", TRUE)); return rv; } SV * porbit_ull_from_ulonglong (unsigned long long val) { SV *rv; SV *result = newSV(0); SvUPGRADE (result, SVt_NV); ULL_VALUE(result) = val; rv = newRV_noinc (result); sv_bless (rv, gv_stashpv ("CORBA::ULongLong", TRUE)); return rv; } SV * porbit_ld_from_longdouble (long double val) { SV *rv; SV *result = newSV(sizeof(long double)); LD_VALUE(result) = val; rv = newRV_noinc (result); sv_bless (rv, gv_stashpv ("CORBA::LongDouble", TRUE)); return rv; } /* Some rough-and-ready equivalents for library functions to convert * long longs and long doubles to and from strings The long double * stuff is in particular somewhat bad. It usually gets the last * bit wrong (glibc uses a multiple precision library for this, it may * be hard to do better sticking within the long double type) * * The goal here is portability without having to know the details * of the layout of the long double type. */ long long porbit_longlong_from_string (const char *str) { long long val = 0; int negate = 0; while (*str) { if (*str == '-') { negate = 1; str++; break; } else if (*str == '+') { str++; break; } else if (isspace (*str)) { str++; } else break; } while (*str) { if (isdigit (*str)) { val *= 10; val += *str - '0'; } else if (!isspace (*str)) break; str++; } if (negate) val = -val; return val; } char * porbit_longlong_to_string (long long val) { int length = 2; char *str; int n = 0; int negate = 0; int m; char tmp; New (7554, str, 3, char); if (val < 0) { negate = 1; str[n] = '-'; n++; val = -val; } while (val || (n == 0)) { str[n] = (val % 10) + '0'; val /= 10; n++; if (n >= length) { length *= 2; Renew (str, length+1, char); } } str[n] = '\0'; m = negate ? 1 : 0; while (--n > m) { tmp = str[m]; str[m] = str[n]; str[n] = tmp; m++; } return str; } unsigned long long porbit_ulonglong_from_string (const char *str) { unsigned long long val = 0; while (*str) { if (*str == '+') { str++; break; } else if (isspace (*str)) { str++; } else break; } while (*str) { if (isdigit (*str)) { val *= 10; val += *str - '0'; } else if (!isspace (*str)) break; str++; } return val; } char * porbit_ulonglong_to_string (unsigned long long val) { int length = 2; char *str; int n = 0; int m; char tmp; New (7554, str, 3, char); while (val || (n == 0)) { str[n] = (val % 10) + '0'; val /= 10; n++; if (n >= length) { length *= 2; Renew (str, length+1, char); } } str[n] = '\0'; m = 0; while (--n > m) { tmp = str[m]; str[m] = str[n]; str[n] = tmp; m++; } return str; } long double porbit_longdouble_from_string (const char *str) { int decimal = 0x7FFFFFFF; int exponent = 0; int negate = 0; int enegate = 0; long double val = 0; long double divisor = 1.0; long double d; int i = 0; /* Skip leading white space */ while (*str) { if (!isspace (*str)) break; str++; } /* read sign */ if (*str == '-') { negate = 1; str++; } else if (*str == '+') str++; /* read decimal digits */ while (*str) { if (isdigit (*str)) { val *= 10; val += (*str - '0'); i++; } else if (*str == '.') decimal = i; else break; str++; } /* read exponent */ if (*str == 'e' || (*str == 'E')) { enegate = 0; str++; if (*str == '-') { enegate = 1; str++; } while (*str) { if (isdigit (*str)) { exponent *= 10; exponent += *str - '0'; str++; } else break; } if (enegate) exponent = -exponent; } if (decimal <= i) exponent -= (i - decimal); if (exponent < 0) { exponent = -exponent; enegate = 1; } else enegate = 0; if (negate) val = - val; divisor = 1.0; d = 10.0; while (exponent) { if (exponent & 1) divisor *= d; exponent >>= 1; d *= d; } if (enegate) val /= divisor; else val *= divisor; return val; } char * porbit_longdouble_to_string (long double val) { int count; int invert; long double a; long double divisor, oldd; long double limit; long double small; int e; int i; int exp, oldexp; int length = 6; char *str; int n = 0; New (7554, str, length, char); if (val < 0) { str[n] = '-'; n++; } if (val == 0) { strcat (&str[n], "0.e0"); return str; } if (val < 1.0) { limit = 1 / val; invert = 1; } else { if (val * 2 == val) { /* infinity */ strcat (&str[n], "Inf"); return str; } limit = val; invert = 0; } divisor = oldd = 1.0; exp = oldexp = 0; /* Use a modified binary search to find the exponent. * we grow the exponent by powers of two, and when we * overshoot, start again from the next to last point. */ if (limit > 1.0) { do { divisor = oldd; exp = oldexp; a = 10.0; e = 1; do { oldd = divisor; divisor *= a; a = a*a; oldexp = exp; exp += e; e *= 2; } while (divisor < limit); } while (e != 2); } /* Scale the result */ if (invert) { exp = -exp; val *= divisor; } else { if (divisor == limit) { val /= divisor; } else { exp = oldexp; val /= oldd; } } /* We now have val normalized to 1.0 <= val < 10.0 */ /* Write out digits, until it almost certainly doesn't make * a difference. (We go a few digits over, so that we don't * have to worry about rounding). This could most likely * be done better. */ small = 10.0; i = 0; count = 2; while (count != 0) { int digit = (int)val; if (1.0 + small == 1.0) count--; str[n++] = '0' + digit; if (n+4 >= length) { /* leave space for . and e - */ length *= 2; Renew (str, length, char); } val -= digit; small /= 10.; val *= 10.; if (i++ == 0) str[n++] = '.'; } str[n++] = 'e'; if (exp < 0) { str[n++] = '-'; exp = -exp; } do { str[n++] = (exp % 10) + '0'; exp /= 10; if (n + 1 >= length) { length *= 2; Renew (str, length, char); } } while (exp); str[n] = '\0'; return str; }