File floattostring.h
File List > code_source > templat > include > TempLat > util > floattostring.h
Go to the documentation of this file
#ifndef TEMPLAT_UTIL_FLOATTOSTRING_H
#define TEMPLAT_UTIL_FLOATTOSTRING_H
/* This file is part of TempLat, available at https://cosmolattice.github.io/templat .
Copyright 2021-2026 The TempLat authors, see AUTHORS.md.
Released under the MIT license, see LICENSE.md. */
// File info: Main contributor(s): Wessel Valkenburg, Year: 2019
#include "TempLat/util/stringtrimmer.h"
#include <cmath>
#include <cstring>
#include <string>
namespace TempLat
{
class FloatToString
{
public:
template <typename T>
requires std::is_floating_point_v<T>
static inline std::string format(T value)
{
char buffer[128];
std::memset(buffer, 0, 128);
if ((std::abs(value) > 1e3 || std::abs(value) < 1e-2) && std::abs(value) != 0) {
snprintf(buffer, 128, "%11.2e", value);
stripTrailingZeros(buffer, findThe_E(buffer), '*');
squashChars(buffer, '*');
} else {
if (std::abs(value) < 1e-1)
snprintf(buffer, 128, "%.3f", value);
else if (std::abs(value) < 1)
snprintf(buffer, 128, "%.2f", value);
else
snprintf(buffer, 128, "%.1f", value);
stripTrailingZeros(buffer, buffer + 128, '\0');
}
return StringTrimmer::trimmed(buffer);
}
template <typename T>
requires std::is_floating_point<T>::value
static inline std::string format_more(T value, int ndig)
{
char buffer[128];
std::memset(buffer, 0, 128);
std::string fmat = "%11." + std::to_string(ndig) + "e";
if ((std::abs(value) > 1e3 || std::abs(value) < 1e-2) && std::abs(value) != 0) {
snprintf(buffer, 128, fmat.c_str(), value);
stripTrailingZeros(buffer, findThe_E(buffer), '*');
squashChars(buffer, '*');
} else {
if (std::abs(value) < 1e-1) {
fmat = "%." + std::to_string(ndig) + "f";
snprintf(buffer, 128, fmat.c_str(), value);
} else if (std::abs(value) < 1) {
fmat = "%." + std::to_string(ndig - 1) + "f";
snprintf(buffer, 128, fmat.c_str(), value);
} else {
fmat = "%." + std::to_string(ndig - 2) + "f";
snprintf(buffer, 128, fmat.c_str(), value);
}
stripTrailingZeros(buffer, buffer + 128, '\0');
}
return StringTrimmer::trimmed(buffer);
}
private:
/* Put all member variables and private methods here. These may change arbitrarily. */
static inline void stripTrailingZeros(char *begin, char *end, char replacement)
{
if (end <= begin) return;
bool goOn = true;
for (char *i = end - 1; i >= begin && goOn; --i) {
switch (*i) {
case '\0':
break;
case '0':
/* cut trailing zeros. */
*i = replacement;
break;
case '.':
/* cut trailing dot. and stop. */
*i = replacement;
goOn = false;
break;
default:
/* anything other than zero or dot? Done with the cutting. */
goOn = false;
break;
}
}
}
static inline char *findThe_E(char *string)
{
while (*string != '\0' && *string != 'e' && *string != 'E')
++string;
return string;
}
static inline void squashChars(char *string, char replacement)
{
bool insideBatch = false;
char *batchStart = string;
char *iEnd = string;
/* note that with this definition iEnd is a valid pointer, to the trailing '\0'-char. */
while (*iEnd != '\0')
++iEnd;
for (char *i = string; i < iEnd; ++i) {
if (*i == replacement) {
if (!insideBatch) batchStart = i;
insideBatch = true;
} else {
if (insideBatch) {
insideBatch = false;
/* squash everything between batchStart and i */
ptrdiff_t batchSize = iEnd - i + 1; /* +1, because iEnd is a valid pointer to the trailing '\0'-char. */
std::memmove(batchStart, i, batchSize);
}
}
}
}
};
} // namespace TempLat
#endif