129 lines
4.5 KiB
C
129 lines
4.5 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2009 Ian Piumarta │
|
|
│ │
|
|
│ Permission is hereby granted, free of charge, to any person obtaining a copy │
|
|
│ of this software and associated documentation files (the 'Software'), to │
|
|
│ deal in the Software without restriction, including without limitation the │
|
|
│ rights to use, copy, modify, merge, publish, distribute, and/or sell copies │
|
|
│ of the Software, and to permit persons to whom the Software is furnished to │
|
|
│ do so, provided that the above copyright notice(s) and this permission │
|
|
│ notice appear in all copies of the Software. Inclusion of the above │
|
|
│ copyright notice(s) and this permission notice in supporting documentation │
|
|
│ would be appreciated but is not required. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/assert.h"
|
|
#include "libc/fmt/fmt.h"
|
|
#include "libc/macros.h"
|
|
#include "libc/math.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/str/str.h"
|
|
|
|
asm(".ident\t\"\\n\\n\
|
|
ecvt, fcvt (MIT License)\\n\
|
|
Copyright 2009 Ian Piumarta\"");
|
|
|
|
/**
|
|
* @fileoverview Replacements for the functions ecvt() and fcvt()
|
|
*
|
|
* These functions were recently deprecated in POSIX. The interface and
|
|
* behaviour is identical to the functions that they replace and faster.
|
|
*
|
|
* For details on the use of these functions, see your ecvt(3) manual
|
|
* page. If you don't have one handy, there might still be one available
|
|
* here: http://opengroup.org/onlinepubs/007908799/xsh/ecvt.html
|
|
*
|
|
* @see https://www.piumarta.com/software/fcvt/
|
|
*/
|
|
|
|
static char *Fcvt(double value, int ndigit, int *decpt, int *sign, int fflag) {
|
|
static char buf[128];
|
|
double i;
|
|
uint64_t l, mant;
|
|
int exp2, exp10, ptr;
|
|
memcpy(&l, &value, 8);
|
|
exp2 = (0x7ff & (l >> 52)) - 1023;
|
|
mant = l & 0x000fffffffffffffULL;
|
|
if ((*sign = l >> 63)) value = -value;
|
|
if (exp2 == 0x400) {
|
|
*decpt = 0;
|
|
return mant ? "nan" : "inf";
|
|
}
|
|
exp10 = (value == 0) ? !fflag : (int)ceil(log10(value));
|
|
if (exp10 < -307) exp10 = -307; /* otherwise overflow in pow() */
|
|
value *= pow(10.0, -exp10);
|
|
if (value) {
|
|
while (value < 0.1) {
|
|
value *= 10;
|
|
--exp10;
|
|
}
|
|
while (value >= 1.0) {
|
|
value /= 10;
|
|
++exp10;
|
|
}
|
|
}
|
|
assert(value == 0 || (0.1 <= value && value < 1.0));
|
|
if (fflag) {
|
|
if (ndigit + exp10 < 0) {
|
|
*decpt = -ndigit;
|
|
return "";
|
|
}
|
|
ndigit += exp10;
|
|
}
|
|
*decpt = exp10;
|
|
if (ARRAYLEN(buf) < ndigit + 2) abort();
|
|
ptr = 1;
|
|
#if 0 /* slow and safe (and dreadfully boring) */
|
|
while (ptr <= ndigit) {
|
|
i;
|
|
value = modf(value * 10, &i);
|
|
buf[ptr++] = '0' + (int)i;
|
|
}
|
|
if (value >= 0.5) {
|
|
while (--ptr && ++buf[ptr] > '9') {
|
|
buf[ptr] = '0';
|
|
}
|
|
}
|
|
#else /* faster */
|
|
memcpy(&l, &value, 8);
|
|
exp2 = (0x7ff & (l >> 52)) - 1023;
|
|
assert(value == 0 || (-4 <= exp2 && exp2 <= -1));
|
|
mant = l & 0x000fffffffffffffULL;
|
|
if (exp2 == -1023) {
|
|
++exp2;
|
|
} else {
|
|
mant |= 0x0010000000000000ULL;
|
|
}
|
|
mant <<= (exp2 + 4); /* 56-bit denormalised signifier */
|
|
while (ptr <= ndigit) {
|
|
mant &= 0x00ffffffffffffffULL; /* mod 1.0 */
|
|
mant = (mant << 1) + (mant << 3);
|
|
buf[ptr++] = '0' + (mant >> 56);
|
|
}
|
|
if (mant & 0x0080000000000000ULL) /* 1/2 << 56 */
|
|
while (--ptr && ++buf[ptr] > '9') buf[ptr] = '0';
|
|
#endif
|
|
if (ptr) {
|
|
buf[ndigit + 1] = 0;
|
|
return buf + 1;
|
|
}
|
|
if (fflag) {
|
|
++ndigit;
|
|
++*decpt;
|
|
}
|
|
buf[0] = '1';
|
|
buf[ndigit] = 0;
|
|
return buf;
|
|
}
|
|
|
|
char *ecvt(double value, int ndigit, int *decpt, int *sign) {
|
|
return Fcvt(value, ndigit, decpt, sign, 0);
|
|
}
|
|
|
|
char *fcvt(double value, int ndigit, int *decpt, int *sign) {
|
|
return Fcvt(value, ndigit, decpt, sign, 1);
|
|
}
|