cake
/
libg1m
Archived
1
0
Fork 0
This repository has been archived on 2024-03-16. You can view files and clone it, but cannot push or open issues or pull requests.
libg1m/src/bcd/str.c

138 lines
3.7 KiB
C

/* *****************************************************************************
* bcd/str.c -- make a string out of a libg1m BCD.
* Copyright (C) 2017 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
*
* This file is part of libg1m.
* libg1m is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3.0 of the License,
* or (at your option) any later version.
*
* libg1m is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libg1m; if not, see <http://www.gnu.org/licenses/>.
*
* These functions are experimental!
* I wrote most of this while comparing the results to what I wanted.
* If you think you can write a clean equivalent of this, while understanding
* the theory behind what you do, please try! :)
* ************************************************************************** */
#include <libg1m/internals.h>
/**
* getsize:
* Get the size.
*
* @arg exp the exponent.
* @arg neg whether the number is negative or not.
* @arg offset the offset.
* @arg digits the number of digits.
* @return the size.
*/
static size_t getsize(int exp, int neg, int offset, int digits)
{
size_t sz = digits;
if (neg)
sz++;
if (offset + digits < 0)
sz += -digits - offset; /* zeroes */
if (offset + digits > 0)
sz++; /* point */
if (offset >= 0)
sz += 1 /* leading zero */ + offset;
if (exp) {
sz++; /* e */
if (exp < 0) sz += 1;
int eexp = abs(exp);
if (eexp >= 100) sz += 3;
else if (eexp >= 10) sz += 2;
else sz += 1;
}
return (sz);
}
/**
* g1m_bcdtoa:
* Make a string out of a BCD number.
*
* If the buffer is not big enough, the number will not be written down.
* Will terminate the buffer if the string is written.
*
* @arg bcd the BCD.
* @arg buf the buffer.
* @arg len the length.
* @return the required length if needed, otherwise, zero.
*/
size_t g1m_bcdtoa(const g1m_bcd_t *bcd, char *buf, size_t len)
{
/* get base digit */
int exp = g1m_bcd_exponent(bcd), offset = 0;
int leftdigits = g1m_bcd_precision(bcd);
const char *digits = bcd->g1m_bcd_mant;
for (; leftdigits > 0; leftdigits--) {
if (*digits) break;
exp--;
digits++;
}
/* get number of digits, check if zero */
for (; leftdigits && !digits[leftdigits - 1]; leftdigits--);
if (!leftdigits) {
int l = (len >= 2) ? 0 : 2;
memcpy(buf, "0", 2 - l);
return (len);
}
/* try to play with the settings to make a more acceptable output */
int neg = !!g1m_bcd_is_negative(bcd);
int acceptable_alt = max(0, 5 - leftdigits);
if (-acceptable_alt < exp && exp <= 5) {
offset -= exp;
exp = 0;
}
if (offset >= 0) {
exp -= offset + 1;
offset = -1;
}
/* calculate the size */
size_t sz = getsize(exp, neg, offset, leftdigits);
/* check if there's enough space in the buffer */
if (sz > len) return (sz);
/* then fill */
int origdigits = leftdigits;
if (neg)
*buf++ = '-';
for (; leftdigits && offset < 0; offset++, leftdigits--)
*buf++ = *digits++ + '0';
for (; offset < 0; offset++)
*buf++ = '0';
if (leftdigits == origdigits)
*buf++ = '0';
if (offset > 0 || leftdigits)
*buf++ = '.';
for (; offset > 0; offset--)
*buf++ = '0';
while (leftdigits--)
*buf++ = *digits++ + '0';
if (exp) {
*buf++ = 'e';
if (exp < 0) { *buf++ = '-'; exp = -exp; }
if (exp >= 100) *buf++ = exp / 100 + '0';
if (exp >= 10) *buf++ = (exp % 100) / 10 + '0';
*buf++ = exp % 10 + '0';
}
*buf = 0;
/* it's done! */
return (0);
}