3c99391d93
In some cases the license-identification header was missing, so I added that as well. Replaced the remaining headers that specifically identified "Versions 3.13.7 and higher". Makefiles and the build system were deliberately excluded.
524 lines
11 KiB
C
524 lines
11 KiB
C
/*************************************************************************\
|
|
* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne
|
|
* National Laboratory.
|
|
* Copyright (c) 2002 The Regents of the University of California, as
|
|
* Operator of Los Alamos National Laboratory.
|
|
* SPDX-License-Identifier: EPICS
|
|
* EPICS BASE is distributed subject to a Software License Agreement found
|
|
* in file LICENSE that is included with this distribution.
|
|
\*************************************************************************/
|
|
/* Fast numeric to string conversions
|
|
*
|
|
* Original Authors:
|
|
* Bob Dalesio, Mark Anderson and Marty Kraimer
|
|
* Date: 12 January 1993
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
|
|
#include "cvtFast.h"
|
|
#include "epicsMath.h"
|
|
#include "epicsStdio.h"
|
|
|
|
/*
|
|
* These routines convert numbers up to +/- 10,000,000.
|
|
* They defer to sprintf() for numbers requiring more than
|
|
* 8 places of precision.
|
|
*/
|
|
static epicsInt32 frac_multiplier[] =
|
|
{1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
|
|
|
|
int cvtFloatToString(float flt_value, char *pdest,
|
|
epicsUInt16 precision)
|
|
{
|
|
int got_one, i;
|
|
epicsInt32 whole, iplace, number, fraction, fplace;
|
|
float ftemp;
|
|
char *startAddr;
|
|
|
|
/* can this routine handle this conversion */
|
|
if (isnan(flt_value) || precision > 8 ||
|
|
flt_value > 10000000.0 || flt_value < -10000000.0) {
|
|
if (precision > 8 || flt_value >= 1e8 || flt_value <= -1e8) {
|
|
if (precision > 12) precision = 12; /* FIXME */
|
|
sprintf(pdest, "%*.*e", precision+6, precision, (double) flt_value);
|
|
} else {
|
|
if (precision > 3) precision = 3; /* FIXME */
|
|
sprintf(pdest, "%.*f", precision, (double) flt_value);
|
|
}
|
|
return((int)strlen(pdest));
|
|
}
|
|
startAddr = pdest;
|
|
|
|
/* determine the sign */
|
|
if (flt_value < 0){
|
|
*pdest++ = '-';
|
|
flt_value = -flt_value;
|
|
};
|
|
|
|
/* remove the whole number portion */
|
|
whole = (epicsInt32)flt_value;
|
|
ftemp = flt_value - whole;
|
|
|
|
/* multiplier to convert fractional portion to integer */
|
|
fplace = frac_multiplier[precision];
|
|
fraction = (epicsInt32)(ftemp * fplace * 10);
|
|
fraction = (fraction + 5) / 10; /* round up */
|
|
|
|
/* determine rounding into the whole number portion */
|
|
if ((fraction / fplace) >= 1){
|
|
whole++;
|
|
fraction -= fplace;
|
|
}
|
|
|
|
/* whole numbers */
|
|
got_one = 0;
|
|
for (iplace = 10000000; iplace >= 1; iplace /= 10){
|
|
if (whole >= iplace){
|
|
got_one = 1;
|
|
number = whole / iplace;
|
|
whole = whole - (number * iplace);
|
|
*pdest = number + '0';
|
|
pdest++;
|
|
}else if (got_one){
|
|
*pdest = '0';
|
|
pdest++;
|
|
}
|
|
}
|
|
if (!got_one){
|
|
*pdest = '0';
|
|
pdest++;
|
|
}
|
|
|
|
/* fraction */
|
|
if (precision > 0){
|
|
/* convert fractional portional to ASCII */
|
|
*pdest = '.';
|
|
pdest++;
|
|
for (fplace /= 10, i = precision; i > 0; fplace /= 10,i--){
|
|
number = fraction / fplace;
|
|
fraction -= number * fplace;
|
|
*pdest = number + '0';
|
|
pdest++;
|
|
}
|
|
}
|
|
*pdest = 0;
|
|
|
|
return((int)(pdest - startAddr));
|
|
}
|
|
|
|
int cvtDoubleToString(
|
|
double flt_value,
|
|
char *pdest,
|
|
epicsUInt16 precision)
|
|
{
|
|
epicsUInt16 got_one,i;
|
|
epicsInt32 whole,iplace,number,fraction,fplace;
|
|
double ftemp;
|
|
char *startAddr;
|
|
|
|
/* can this routine handle this conversion */
|
|
if (isnan(flt_value) || precision > 8 || flt_value > 10000000.0 || flt_value < -10000000.0) {
|
|
if (precision > 8 || flt_value > 1e16 || flt_value < -1e16) {
|
|
if(precision>17) precision=17;
|
|
sprintf(pdest,"%*.*e",precision+7,precision,
|
|
flt_value);
|
|
} else {
|
|
if(precision>3) precision=3;
|
|
sprintf(pdest,"%.*f",precision,flt_value);
|
|
}
|
|
return((int)strlen(pdest));
|
|
}
|
|
startAddr = pdest;
|
|
|
|
/* determine the sign */
|
|
if (flt_value < 0){
|
|
*pdest++ = '-';
|
|
flt_value = -flt_value;
|
|
};
|
|
|
|
/* remove the whole number portion */
|
|
whole = (epicsInt32)flt_value;
|
|
ftemp = flt_value - whole;
|
|
|
|
/* multiplier to convert fractional portion to integer */
|
|
fplace = frac_multiplier[precision];
|
|
fraction = (epicsInt32)(ftemp * fplace * 10);
|
|
fraction = (fraction + 5) / 10; /* round up */
|
|
|
|
/* determine rounding into the whole number portion */
|
|
if ((fraction / fplace) >= 1){
|
|
whole++;
|
|
fraction -= fplace;
|
|
}
|
|
|
|
/* whole numbers */
|
|
got_one = 0;
|
|
for (iplace = 10000000; iplace >= 1; iplace /= 10){
|
|
if (whole >= iplace){
|
|
got_one = 1;
|
|
number = whole / iplace;
|
|
whole = whole - (number * iplace);
|
|
*pdest = number + '0';
|
|
pdest++;
|
|
}else if (got_one){
|
|
*pdest = '0';
|
|
pdest++;
|
|
}
|
|
}
|
|
if (!got_one){
|
|
*pdest = '0';
|
|
pdest++;
|
|
}
|
|
|
|
/* fraction */
|
|
if (precision > 0){
|
|
/* convert fractional portional to ASCII */
|
|
*pdest = '.';
|
|
pdest++;
|
|
for (fplace /= 10, i = precision; i > 0; fplace /= 10,i--){
|
|
number = fraction / fplace;
|
|
fraction -= number * fplace;
|
|
*pdest = number + '0';
|
|
pdest++;
|
|
}
|
|
}
|
|
*pdest = 0;
|
|
|
|
return((int)(pdest - startAddr));
|
|
}
|
|
|
|
/*
|
|
* These routines are provided for backwards compatibility,
|
|
* extensions such as MEDM, edm and histtool use them.
|
|
*/
|
|
|
|
/*
|
|
* cvtFloatToExpString
|
|
*
|
|
* Converts a float to a %e formatted string
|
|
*/
|
|
int cvtFloatToExpString(float val, char *pdest, epicsUInt16 precision)
|
|
{
|
|
return epicsSnprintf(pdest, MAX_STRING_SIZE, "%.*e", precision, val);
|
|
}
|
|
|
|
/*
|
|
* cvtFloatToCompactString
|
|
*
|
|
* Converts a float to a %g formatted string.
|
|
* The result uses %f notation for 10e-4 < |value| < 10e+4,
|
|
* otherwise %e notation.
|
|
*/
|
|
int cvtFloatToCompactString(float val, char *pdest, epicsUInt16 precision)
|
|
{
|
|
if ((val < 1.e4 && val > 1.e-4) ||
|
|
(val > -1.e4 && val < -1.e-4) ||
|
|
val == 0.0)
|
|
return cvtFloatToString(val, pdest, precision);
|
|
|
|
return cvtFloatToExpString(val, pdest, precision);
|
|
}
|
|
|
|
|
|
/*
|
|
* cvtDoubleToExpString
|
|
*
|
|
* Converts a double to a %e formatted string
|
|
*/
|
|
|
|
int cvtDoubleToExpString(double val, char *pdest, epicsUInt16 precision)
|
|
{
|
|
return epicsSnprintf(pdest, MAX_STRING_SIZE, "%.*e", precision, val);
|
|
}
|
|
|
|
|
|
/*
|
|
* cvtDoubleToCompactString
|
|
*
|
|
* Converts a double to %g formatted string.
|
|
* The result uses %f notation for 10e-4 < |value| < 10e+4,
|
|
* otherwise %e notation.
|
|
*/
|
|
int cvtDoubleToCompactString(double val, char *pdest, epicsUInt16 precision)
|
|
{
|
|
if ((val < 1.e4 && val > 1.e-4) ||
|
|
(val > -1.e4 && val < -1.e-4) ||
|
|
val == 0.0)
|
|
return cvtDoubleToString(val, pdest, precision);
|
|
|
|
return cvtDoubleToExpString(val, pdest, precision);
|
|
}
|
|
|
|
|
|
/* Integer conversion primitives */
|
|
|
|
static size_t
|
|
UInt32ToDec(epicsUInt32 val, char *pdest)
|
|
{
|
|
int i;
|
|
char digit[10];
|
|
size_t len;
|
|
|
|
for (i = 0; val; i++) {
|
|
epicsUInt32 tenth = val / 10;
|
|
|
|
digit[i] = val - tenth * 10 + '0';
|
|
val = tenth;
|
|
}
|
|
len = i;
|
|
|
|
while (i > 0)
|
|
*pdest++ = digit[--i];
|
|
|
|
*pdest = 0;
|
|
return len;
|
|
}
|
|
|
|
static size_t
|
|
UInt32ToBase(epicsUInt32 val, char *pdest, int base)
|
|
{
|
|
int i;
|
|
char digit, digits[32];
|
|
size_t len;
|
|
|
|
for (i = 0; val; i++) {
|
|
epicsUInt32 tenth = val / base;
|
|
|
|
digit = val - tenth * base;
|
|
digits[i] = digit < 10 ? digit + '0' : digit - 10 + 'a';
|
|
val = tenth;
|
|
}
|
|
len = i;
|
|
|
|
while (i > 0)
|
|
*pdest++ = digits[--i];
|
|
|
|
*pdest = 0;
|
|
return len;
|
|
}
|
|
|
|
static size_t
|
|
UInt64ToDec(epicsUInt64 val, char *pdest)
|
|
{
|
|
int i;
|
|
char digit[20];
|
|
size_t len;
|
|
|
|
for (i = 0; val; i++) {
|
|
epicsUInt64 tenth = val / 10;
|
|
|
|
digit[i] = val - tenth * 10 + '0';
|
|
val = tenth;
|
|
}
|
|
|
|
len = i;
|
|
while (i > 0)
|
|
*pdest++ = digit[--i];
|
|
|
|
*pdest = 0;
|
|
return len;
|
|
}
|
|
|
|
static size_t
|
|
UInt64ToBase(epicsUInt64 val, char *pdest, int base)
|
|
{
|
|
int i;
|
|
char digit, digits[64];
|
|
size_t len;
|
|
|
|
for (i = 0; val; i++) {
|
|
epicsUInt64 tenth = val / base;
|
|
|
|
digit = val - tenth * base;
|
|
digits[i] = digit < 10 ? digit + '0' : digit - 10 + 'a';
|
|
val = tenth;
|
|
}
|
|
len = i;
|
|
|
|
while (i > 0)
|
|
*pdest++ = digits[--i];
|
|
|
|
*pdest = 0;
|
|
return len;
|
|
}
|
|
|
|
|
|
/* Integer conversion routines */
|
|
|
|
size_t
|
|
cvtUInt32ToString(epicsUInt32 val, char *pdest)
|
|
{
|
|
if (val == 0) {
|
|
*pdest++ = '0';
|
|
*pdest = 0;
|
|
return 1;
|
|
}
|
|
|
|
return UInt32ToDec(val, pdest);
|
|
}
|
|
|
|
size_t
|
|
cvtInt32ToString(epicsInt32 val, char *pdest)
|
|
{
|
|
if (val == 0) {
|
|
*pdest++ = '0';
|
|
*pdest = 0;
|
|
return 1;
|
|
}
|
|
|
|
if (val > 0)
|
|
return UInt32ToDec(val, pdest);
|
|
|
|
if (val == -0x80000000) {
|
|
strcpy(pdest, "-2147483648");
|
|
return strlen(pdest);
|
|
}
|
|
|
|
*pdest++ = '-';
|
|
return 1 + UInt32ToDec(-val, pdest);
|
|
}
|
|
|
|
|
|
size_t
|
|
cvtUInt64ToString(epicsUInt64 val, char *pdest)
|
|
{
|
|
if (val == 0) {
|
|
*pdest++ = '0';
|
|
*pdest = 0;
|
|
return 1;
|
|
}
|
|
|
|
return UInt64ToDec(val, pdest);
|
|
}
|
|
|
|
size_t
|
|
cvtInt64ToString(epicsInt64 val, char *pdest)
|
|
{
|
|
if (val == 0) {
|
|
*pdest++ = '0';
|
|
*pdest = 0;
|
|
return 1;
|
|
}
|
|
|
|
if (val > 0)
|
|
return UInt64ToDec(val, pdest);
|
|
|
|
if (val == -0x8000000000000000LL) {
|
|
strcpy(pdest, "-9223372036854775808");
|
|
return strlen(pdest);
|
|
}
|
|
|
|
*pdest++ = '-';
|
|
return 1 + UInt64ToDec(-val, pdest);
|
|
}
|
|
|
|
|
|
size_t
|
|
cvtInt32ToHexString(epicsInt32 val, char *pdest)
|
|
{
|
|
if (val < 0)
|
|
*pdest++ = '-';
|
|
|
|
*pdest++ = '0';
|
|
*pdest++ = 'x';
|
|
|
|
if (val == 0) {
|
|
*pdest++ = '0';
|
|
*pdest = 0;
|
|
return 3;
|
|
}
|
|
|
|
if (val > 0)
|
|
return 2 + UInt32ToBase(val, pdest, 16);
|
|
|
|
if (val == -0x80000000) {
|
|
strcpy(pdest, "80000000");
|
|
return 11;
|
|
}
|
|
|
|
return 3 + UInt32ToBase(-val, pdest, 16);
|
|
}
|
|
|
|
size_t
|
|
cvtUInt32ToHexString(epicsUInt32 val, char *pdest)
|
|
{
|
|
*pdest++ = '0';
|
|
*pdest++ = 'x';
|
|
|
|
if (val == 0) {
|
|
*pdest++ = '0';
|
|
*pdest = 0;
|
|
return 3;
|
|
}
|
|
|
|
return 2 + UInt32ToBase(val, pdest, 16);
|
|
}
|
|
|
|
size_t
|
|
cvtInt32ToOctalString(epicsInt32 val, char *pdest)
|
|
{
|
|
if (val == 0) {
|
|
*pdest++ = '0';
|
|
*pdest = 0;
|
|
return 1;
|
|
}
|
|
|
|
if (val > 0) {
|
|
*pdest++ = '0';
|
|
return 1 + UInt32ToBase(val, pdest, 8);
|
|
}
|
|
|
|
if (val == -0x80000000) {
|
|
strcpy(pdest, "-020000000000");
|
|
return strlen(pdest);
|
|
}
|
|
|
|
*pdest++ = '-';
|
|
*pdest++ = '0';
|
|
return 2 + UInt32ToBase(-val, pdest, 8);
|
|
}
|
|
|
|
size_t
|
|
cvtInt64ToHexString(epicsInt64 val, char *pdest)
|
|
{
|
|
if (val < 0)
|
|
*pdest++ = '-';
|
|
|
|
*pdest++ = '0';
|
|
*pdest++ = 'x';
|
|
|
|
if (val == 0) {
|
|
*pdest++ = '0';
|
|
*pdest = 0;
|
|
return 3;
|
|
}
|
|
|
|
if (val > 0)
|
|
return 2 + UInt64ToBase(val, pdest, 16);
|
|
|
|
if (val == -0x8000000000000000LL) {
|
|
strcpy(pdest, "8000000000000000");
|
|
return 19;
|
|
}
|
|
|
|
return 3 + UInt64ToBase(-val, pdest, 16);
|
|
}
|
|
|
|
size_t
|
|
cvtUInt64ToHexString(epicsUInt64 val, char *pdest)
|
|
{
|
|
*pdest++ = '0';
|
|
*pdest++ = 'x';
|
|
|
|
if (val == 0) {
|
|
*pdest++ = '0';
|
|
*pdest = 0;
|
|
return 3;
|
|
}
|
|
|
|
return 2 + UInt64ToBase(val, pdest, 16);
|
|
}
|
|
|