From c51c359b7601f4f071444a4579d1678b5aac7d06 Mon Sep 17 00:00:00 2001 From: Jan Breuer <jan.breuer@jaybee.cz> Date: 周一, 09 11月 2015 23:30:07 +0800 Subject: [PATCH] Custom double to string conversion routine --- libscpi/test/test_scpi_utils.c | 46 +++++++++++ libscpi/inc/scpi/config.h | 12 ++ libscpi/src/utils_private.h | 5 + libscpi/src/utils.c | 175 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 234 insertions(+), 4 deletions(-) diff --git a/libscpi/inc/scpi/config.h b/libscpi/inc/scpi/config.h index b57d748..2a8c90c 100644 --- a/libscpi/inc/scpi/config.h +++ b/libscpi/inc/scpi/config.h @@ -97,6 +97,10 @@ #define USE_DEPRECATED_FUNCTIONS 1 #endif +#ifndef USE_CUSTOM_DTOSTR +#define USE_CUSTOM_DTOSTR 0 +#endif + /* Compiler specific */ /* RealView/Keil ARM Compiler, e.g. Cortex-M CPUs */ #if defined(__CC_ARM) @@ -175,14 +179,18 @@ #if HAVE_DTOSTRE #define SCPIDEFINE_floatToStr(v, s, l) strlen(dtostre((double)(v), (s), 6, DTOSTR_PLUS_SIGN | DTOSTR_ALWAYS_SIGN | DTOSTR_UPPERCASE)) +#elif USE_CUSTOM_DTOSTRE +#define SCPIDEFINE_floatToStr(v, s, l) strlen(SCPI_dtostre((v), (s), (l), 6, 0)) #else #define SCPIDEFINE_floatToStr(v, s, l) snprintf((s), (l), "%g", (v)) #endif #if HAVE_DTOSTRE -#define SCPIDEFINE_doubleToStr(v, s, l) strlen(dtostre((v), (s), 6, DTOSTR_PLUS_SIGN | DTOSTR_ALWAYS_SIGN | DTOSTR_UPPERCASE)) +#define SCPIDEFINE_doubleToStr(v, s, l) strlen(dtostre((v), (s), 15, DTOSTR_PLUS_SIGN | DTOSTR_ALWAYS_SIGN | DTOSTR_UPPERCASE)) +#elif USE_CUSTOM_DTOSTRE +#define SCPIDEFINE_doubleToStr(v, s, l) strlen(SCPI_dtostre((v), (s), (l), 15, 0)) #else -#define SCPIDEFINE_doubleToStr(v, s, l) snprintf((s), (l), "%lg", (v)) +#define SCPIDEFINE_doubleToStr(v, s, l) snprintf((s), (l), "%.15lg", (v)) #endif diff --git a/libscpi/src/utils.c b/libscpi/src/utils.c index 1e26e17..f69428d 100644 --- a/libscpi/src/utils.c +++ b/libscpi/src/utils.c @@ -40,6 +40,7 @@ #include <stdlib.h> #include <string.h> #include <ctype.h> +#include <math.h> #include "utils_private.h" #include "scpi/utils.h" @@ -753,3 +754,177 @@ } #endif +// Floating point to string conversion routines +// +// Copyright (C) 2002 Michael Ringgaard. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +static char *scpi_ecvt(double arg, int ndigits, int *decpt, int *sign, char *buf, size_t bufsize) { + int r2; + double fi, fj; + char *p, *p1; + + if (ndigits < 0) ndigits = 0; + if (ndigits >= (int) (bufsize - 1)) ndigits = bufsize - 2; + r2 = 0; + *sign = 0; + p = &buf[0]; + if (arg < 0) { + *sign = 1; + arg = -arg; + } + arg = modf(arg, &fi); + p1 = &buf[bufsize]; + + if (fi != 0) { + p1 = &buf[bufsize]; + while (fi != 0) { + fj = modf(fi / 10, &fi); + *--p1 = (int) ((fj + .03) * 10) + '0'; + r2++; + } + while (p1 < &buf[bufsize]) *p++ = *p1++; + } else if (arg > 0) { + while ((fj = arg * 10) < 1) { + arg = fj; + r2--; + } + } + p1 = &buf[ndigits]; + *decpt = r2; + if (p1 < &buf[0]) { + buf[0] = '\0'; + return buf; + } + while (p <= p1 && p < &buf[bufsize]) { + arg *= 10; + arg = modf(arg, &fj); + *p++ = (int) fj + '0'; + } + if (p1 >= &buf[bufsize]) { + buf[bufsize - 1] = '\0'; + return buf; + } + p = p1; + *p1 += 5; + while (*p1 > '9') { + *p1 = '0'; + if (p1 > buf) { + ++*--p1; + } else { + *p1 = '1'; + (*decpt)++; + } + } + *p = '\0'; + return buf; +} + +#define SCPI_DTOSTRE_BUFFER_SIZE 310 + +char * SCPI_dtostre(double __val, char * __s, size_t __ssize, unsigned char __prec, unsigned char __flags) { + char buffer[SCPI_DTOSTRE_BUFFER_SIZE]; + + int sign = signbit(__val); + char * s = buffer; + int decpt; + if (sign) { + __val = -__val; + s[0] = '-'; + s++; + } else if (!isnan(__val)) { + if (SCPI_DTOSTRE_PLUS_SIGN & __flags) { + s[0] = '+'; + s++; + } else if (SCPI_DTOSTRE_ALWAYS_SIGN & __flags) { + s[0] = ' '; + s++; + } + } + + if (!isfinite(__val)) { + if (isnan(__val)) { + strcpy(s, (__flags & SCPI_DTOSTRE_UPPERCASE) ? "NAN" : "nan"); + } else { + strcpy(s, (__flags & SCPI_DTOSTRE_UPPERCASE) ? "INF" : "inf"); + } + strncpy(__s, buffer, __ssize); + return __s; + } + + scpi_ecvt(__val, __prec, &decpt, &sign, s, SCPI_DTOSTRE_BUFFER_SIZE - 1); + if (decpt > 1 && decpt <= __prec) { + memmove(s + decpt + 1, s + decpt, __prec + 1 - decpt); + s[decpt] = '.'; + decpt = 0; + } else if (decpt > -4 && decpt <= 0) { + decpt = -decpt + 1; + memmove(s + decpt + 1, s, __prec + 1); + memset(s, '0', decpt + 1); + s[1] = '.'; + decpt = 0; + } else { + memmove(s + 2, s + 1, __prec + 1); + s[1] = '.'; + decpt--; + } + + s = &s[__prec]; + while (s[0] == '0') { + s[0] = 0; + s--; + } + if (s[0] == '.') { + s[0] = 0; + s--; + } + + if (decpt != 0) { + s++; + s[0] = 'e'; + s++; + if (decpt != 0) { + if (decpt > 0) { + s[0] = '+'; + } + if (decpt < 0) { + s[0] = '-'; + decpt = -decpt; + } + s++; + } + UInt32ToStrBaseSign(decpt, s, 5, 10, 0); + if (s[1] == 0) { + s[2] = s[1]; + s[1] = s[0]; + s[0] = '0'; + } + } + + strncpy(__s, buffer, __ssize); + return __s; +} diff --git a/libscpi/src/utils_private.h b/libscpi/src/utils_private.h index f864004..b96d73d 100644 --- a/libscpi/src/utils_private.h +++ b/libscpi/src/utils_private.h @@ -69,6 +69,11 @@ scpi_bool_t matchCommand(const char * pattern, const char * cmd, size_t len, int32_t *numbers, size_t numbers_len, int32_t default_value) LOCAL; scpi_bool_t composeCompoundCommand(const scpi_token_t * prev, scpi_token_t * current) LOCAL; +#define SCPI_DTOSTRE_UPPERCASE 1 +#define SCPI_DTOSTRE_ALWAYS_SIGN 2 +#define SCPI_DTOSTRE_PLUS_SIGN 4 + char * SCPI_dtostre(double __val, char * __s, size_t __ssize, unsigned char __prec, unsigned char __flags); + #if !HAVE_STRNLEN size_t BSD_strnlen(const char *s, size_t maxlen) LOCAL; #endif diff --git a/libscpi/test/test_scpi_utils.c b/libscpi/test/test_scpi_utils.c index d25b132..f13f63b 100644 --- a/libscpi/test/test_scpi_utils.c +++ b/libscpi/test/test_scpi_utils.c @@ -225,6 +225,47 @@ CU_ASSERT_STRING_EQUAL(str, "1111111011011100101110101001100001110110010101000011001000010000"); } +static void test_scpi_dtostre() { + const size_t strsize = 49 + 1; + double val[] = {NAN, INFINITY, -INFINITY, 0, + 1, 1.1, 1.01, 1.001, 1.0001, 1.00001, 1.000001, 1.0000001, 1.00000001, + 1.000000001, 1.0000000001, 1.00000000001, 1.000000000001, + 1.0000000000001, 1e-5, 1.1e-5, 1.01e-5, 1.001e-5, 1.0001e-5, 1.00001e-5, + 1.000001e-5, 1.0000001e-5, 1.00000001e-5, 1.000000001e-5, + 1.0000000001e-5, 1.00000000001e-5, 1.000000000001e-5, + 1.0000000000001e-5, 1, 12, 123, 1234, 12345, 123456, 1234567, 12345678, + 123456789, 1234567890, 12345678901, 123456789012, 1234567890123, + 12345678901234, 123456789012345, 1234567890123456, 12345678901234567, + 123456789012345678, 1234567890123456789, 1234567890123456789e1, 1.1, + 10.1, 100.1, 1000.1, 10000.1, 100000.1, 1000000.1, 10000000.1, + 100000000.1, 1000000000.1, 0.1234567890123456789, + 0.01234567890123456789, 0.001234567890123456789, + 0.0001234567890123456789, 0.00001234567890123456789, + 0.000001234567890123456789, 0.0000001234567890123456789, + 0.00000001234567890123456789, 0.00000000123456789, 0.000000000123456789, + 0.0000000000123456789, 0.00000000000123456789, 0.000000000000123456789, + 0.0000000000000123456789, 0.00000000000000123456789, + 0.000000000000000123456789, 0.0000000000000000123456789, + -1e-5, -1.1e-5, -1.01e-5, -1.001e-5, -1.0001e-5, -1.00001e-5, + -1.00001e-6, -1.00001e-10, -1.00001e-20, -1.00001e-50, -1.00001e-100, + -1.00001e-200, -1.00001e-300, -1.00001e6, -1.00001e10, -1.00001e20, + -1.00001e50, -1.00001e100, -1.00001e150, -1.00001e200, -1.00001e300, + 1.7976931348623157e308, 2.2250738585072014e-308, + -1.7976931348623157e308, -2.2250738585072014e-308}; + int N = sizeof (val) / sizeof (*val); + int i; + char str[strsize]; + char ref[strsize]; + size_t len; + + for (i = 0; i < N; i++) { + len = strlen(SCPI_dtostre(val[i], str, strsize, 15, 0)); + snprintf(ref, strsize, "%.15lg", val[i]); + CU_ASSERT(len == strlen(ref)); + CU_ASSERT_STRING_EQUAL(str, ref); + } +} + static void test_floatToStr() { const size_t max = 49 + 1; float val[] = {1, -1, 1.1, -1.1, 1e3, 1e30, -1.3e30, -1.3e-30}; @@ -245,7 +286,7 @@ static void test_doubleToStr() { const size_t max = 49 + 1; double val[] = {1, -1, 1.1, -1.1, 1e3, 1e30, -1.3e30, -1.3e-30}; - int N = sizeof (val) / sizeof (double); + int N = sizeof (val) / sizeof (*val); int i; char str[max]; char ref[max]; @@ -253,7 +294,7 @@ for (i = 0; i < N; i++) { len = SCPI_DoubleToStr(val[i], str, max); - snprintf(ref, max, "%lg", val[i]); + snprintf(ref, max, "%.15lg", val[i]); CU_ASSERT(len == strlen(ref)); CU_ASSERT_STRING_EQUAL(str, ref); } @@ -707,6 +748,7 @@ || (NULL == CU_add_test(pSuite, "UInt32ToStrBase", test_UInt32ToStrBase)) || (NULL == CU_add_test(pSuite, "Int64ToStr", test_Int64ToStr)) || (NULL == CU_add_test(pSuite, "UInt64ToStrBase", test_UInt64ToStrBase)) + || (NULL == CU_add_test(pSuite, "SCPI_dtostre", test_scpi_dtostre)) || (NULL == CU_add_test(pSuite, "floatToStr", test_floatToStr)) || (NULL == CU_add_test(pSuite, "doubleToStr", test_doubleToStr)) || (NULL == CU_add_test(pSuite, "strBaseToInt32", test_strBaseToInt32)) -- Gitblit v1.9.1