mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 17:19:03 -04:00
for real tho
This commit is contained in:
parent
208077b95c
commit
64462a11ac
59
src/lib/enmetric.c
Normal file
59
src/lib/enmetric.c
Normal file
@ -0,0 +1,59 @@
|
||||
#include <string.h>
|
||||
#include "notcurses.h"
|
||||
|
||||
const char *enmetric(uintmax_t val, unsigned decimal, char *buf, int omitdec,
|
||||
unsigned mult, int uprefix){
|
||||
const char prefixes[] = "KMGTPEZY"; // 10^21-1 encompasses 2^64-1
|
||||
unsigned consumed = 0;
|
||||
uintmax_t dv;
|
||||
|
||||
if(decimal == 0 || mult == 0){
|
||||
return NULL;
|
||||
}
|
||||
dv = mult;
|
||||
// FIXME verify that input < 2^89, wish we had static_assert() :/
|
||||
while((val / decimal) >= dv && consumed < strlen(prefixes)){
|
||||
dv *= mult;
|
||||
++consumed;
|
||||
if(UINTMAX_MAX / dv < mult){ // near overflow--can't scale dv again
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(dv != mult){ // if consumed == 0, dv must equal mult
|
||||
int sprintfed;
|
||||
if(val / dv > 0){
|
||||
++consumed;
|
||||
}else{
|
||||
dv /= mult;
|
||||
}
|
||||
val /= decimal;
|
||||
// Remainder is val % dv, but we want a percentage as scaled integer.
|
||||
// Ideally we would multiply by 100 and divide the result by dv, for
|
||||
// maximum accuracy (dv might not be a multiple of 10--it is not for
|
||||
// 1,024). That can overflow with large 64-bit values, but we can first
|
||||
// divide both sides by mult, and then scale by 100.
|
||||
if(omitdec && (val % dv) == 0){
|
||||
sprintfed = sprintf(buf, "%ju%c", val / dv,
|
||||
prefixes[consumed - 1]);
|
||||
}else{
|
||||
uintmax_t remain = (dv == mult) ?
|
||||
(val % dv) * 100 / dv :
|
||||
((val % dv) / mult * 100) / (dv / mult);
|
||||
sprintfed = sprintf(buf, "%ju.%02ju%c",
|
||||
val / dv,
|
||||
remain,
|
||||
prefixes[consumed - 1]);
|
||||
}
|
||||
if(uprefix){
|
||||
buf[sprintfed] = uprefix;
|
||||
buf[sprintfed + 1] = '\0';
|
||||
}
|
||||
}else{ // unscaled output, consumed == 0, dv == mult
|
||||
if(omitdec && val % decimal == 0){
|
||||
sprintf(buf, "%ju", val / decimal);
|
||||
}else{
|
||||
sprintf(buf, "%ju.%02ju", val / decimal, val % decimal);
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
300
tests/enmetric.cpp
Normal file
300
tests/enmetric.cpp
Normal file
@ -0,0 +1,300 @@
|
||||
#include "main.h"
|
||||
#include <cfenv>
|
||||
#include <iostream>
|
||||
|
||||
TEST(NotcursesPrefix, CornerInts) {
|
||||
char buf[PREFIXSTRLEN + 1];
|
||||
enmetric(0, 1, buf, 0, 1000, '\0');
|
||||
EXPECT_STREQ("0.00", buf);
|
||||
enmetric(0, 1, buf, 0, 1024, 'i');
|
||||
EXPECT_STREQ("0.00", buf); // no suffix on < mult
|
||||
enmetric(1, 1, buf, 0, 1000, '\0');
|
||||
EXPECT_STREQ("1.00", buf);
|
||||
enmetric(1, 1, buf, 0, 1024, 'i');
|
||||
EXPECT_STREQ("1.00", buf);
|
||||
enmetric(0, 1, buf, 1, 1000, '\0');
|
||||
EXPECT_STREQ("0", buf);
|
||||
enmetric(0, 1, buf, 1, 1024, 'i');
|
||||
EXPECT_STREQ("0", buf); // no suffix on < mult
|
||||
enmetric(1, 1, buf, 1, 1000, '\0');
|
||||
EXPECT_STREQ("1", buf);
|
||||
enmetric(1, 1, buf, 1, 1024, 'i');
|
||||
EXPECT_STREQ("1", buf);
|
||||
enmetric(999, 1, buf, 1, 1000, '\0');
|
||||
EXPECT_STREQ("999", buf);
|
||||
enmetric(1000, 1, buf, 1, 1000, '\0');
|
||||
EXPECT_STREQ("1K", buf);
|
||||
enmetric(1000, 1, buf, 1, 1000, 'i');
|
||||
EXPECT_STREQ("1Ki", buf);
|
||||
enmetric(1000, 1, buf, 1, 1024, 'i');
|
||||
EXPECT_STREQ("1000", buf); // FIXME should be 0.977Ki
|
||||
enmetric(1023, 1, buf, 1, 1000, '\0');
|
||||
EXPECT_STREQ("1.02K", buf);
|
||||
enmetric(1023, 1, buf, 1, 1024, 'i');
|
||||
EXPECT_STREQ("1023", buf);
|
||||
enmetric(1024, 1, buf, 1, 1000, '\0');
|
||||
EXPECT_STREQ("1.02K", buf);
|
||||
enmetric(1024, 1, buf, 1, 1024, 'i');
|
||||
EXPECT_STREQ("1Ki", buf);
|
||||
enmetric(1025, 1, buf, 1, 1000, '\0');
|
||||
EXPECT_STREQ("1.02K", buf);
|
||||
enmetric(1025, 1, buf, 0, 1024, 'i');
|
||||
EXPECT_STREQ("1.00Ki", buf);
|
||||
enmetric(1025, 1, buf, 1, 1024, 'i');
|
||||
EXPECT_STREQ("1.00Ki", buf);
|
||||
enmetric(4096, 1, buf, 1, 1000, '\0');
|
||||
EXPECT_STREQ("4.09K", buf);
|
||||
enmetric(4096, 1, buf, 1, 1024, 'i');
|
||||
EXPECT_STREQ("4Ki", buf);
|
||||
}
|
||||
|
||||
TEST(NotcursesPrefix, Maxints) {
|
||||
char buf[PREFIXSTRLEN + 1];
|
||||
// FIXME these will change based on the size of intmax_t and uintmax_t
|
||||
enmetric(INTMAX_MAX - 1, 1, buf, 0, 1000, '\0');
|
||||
EXPECT_STREQ("9.22E", buf);
|
||||
enmetric(INTMAX_MAX, 1, buf, 0, 1000, '\0');
|
||||
EXPECT_STREQ("9.22E", buf);
|
||||
enmetric(UINTMAX_MAX - 1, 1, buf, 0, 1000, '\0');
|
||||
EXPECT_STREQ("18.44E", buf);
|
||||
enmetric(UINTMAX_MAX, 1, buf, 0, 1000, '\0');
|
||||
EXPECT_STREQ("18.44E", buf);
|
||||
}
|
||||
|
||||
TEST(NotcursesPrefix, Maxints1024) {
|
||||
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
|
||||
char buf[PREFIXSTRLEN + 1], gold[PREFIXSTRLEN + 1];
|
||||
// FIXME these will change based on the size of intmax_t and uintmax_t
|
||||
enmetric(INTMAX_MAX - 1, 1, buf, 0, 1024, 'i');
|
||||
sprintf(gold, "%.2fEi", ((double)(INTMAX_MAX - (1ull << 53))) / (1ull << 60));
|
||||
EXPECT_STREQ(gold, buf);
|
||||
enmetric(INTMAX_MAX + 1ull, 1, buf, 0, 1024, 'i');
|
||||
sprintf(gold, "%.2fEi", ((double)(INTMAX_MAX + 1ull)) / (1ull << 60));
|
||||
EXPECT_STREQ(gold, buf);
|
||||
enmetric(UINTMAX_MAX - 1, 1, buf, 0, 1024, 'i');
|
||||
EXPECT_STREQ("15.99Ei", buf);
|
||||
enmetric(UINTMAX_MAX, 1, buf, 0, 1024, 'i');
|
||||
EXPECT_STREQ("15.99Ei", buf);
|
||||
enmetric(UINTMAX_MAX - (1ull << 53), 1, buf, 0, 1024, 'i');
|
||||
sprintf(gold, "%.2fEi", ((double)UINTMAX_MAX - (1ull << 53)) / (1ull << 60));
|
||||
EXPECT_STREQ(gold, buf);
|
||||
}
|
||||
|
||||
const char suffixes[] = "\0KMGTPE";
|
||||
|
||||
TEST(NotcursesPrefix, PowersOfTen) {
|
||||
char gold[PREFIXSTRLEN + 1];
|
||||
char buf[PREFIXSTRLEN + 1];
|
||||
uintmax_t goldval = 1;
|
||||
uintmax_t val = 1;
|
||||
size_t i = 0;
|
||||
do{
|
||||
enmetric(val, 1, buf, 0, 1000, '\0');
|
||||
const int sidx = i / 3;
|
||||
snprintf(gold, sizeof(gold), "%ju.00%c", goldval, suffixes[sidx]);
|
||||
EXPECT_STREQ(gold, buf);
|
||||
if(UINTMAX_MAX / val < 10){
|
||||
break;
|
||||
}
|
||||
val *= 10;
|
||||
if((goldval *= 10) == 1000){
|
||||
goldval = 1;
|
||||
}
|
||||
}while(++i < sizeof(suffixes) * 3);
|
||||
// If we ran through all our suffixes, that's a problem
|
||||
EXPECT_GT(sizeof(suffixes) * 3, i);
|
||||
}
|
||||
|
||||
TEST(NotcursesPrefix, PowersOfTenNoDec) {
|
||||
char gold[PREFIXSTRLEN + 1];
|
||||
char buf[PREFIXSTRLEN + 1];
|
||||
uintmax_t goldval = 1;
|
||||
uintmax_t val = 1;
|
||||
size_t i = 0;
|
||||
do{
|
||||
enmetric(val, 1, buf, 1, 1000, '\0');
|
||||
const int sidx = i / 3;
|
||||
snprintf(gold, sizeof(gold), "%ju%c", goldval, suffixes[sidx]);
|
||||
EXPECT_STREQ(gold, buf);
|
||||
if(UINTMAX_MAX / val < 10){
|
||||
break;
|
||||
}
|
||||
val *= 10;
|
||||
if((goldval *= 10) == 1000){
|
||||
goldval = 1;
|
||||
}
|
||||
}while(++i < sizeof(suffixes) * 3);
|
||||
// If we ran through all our suffixes, that's a problem
|
||||
EXPECT_GT(sizeof(suffixes) * 3, i);
|
||||
}
|
||||
|
||||
TEST(NotcursesPrefix, PowersOfTwo) {
|
||||
char gold[BPREFIXSTRLEN + 1];
|
||||
char buf[BPREFIXSTRLEN + 1];
|
||||
uintmax_t goldval = 1;
|
||||
uintmax_t val = 1;
|
||||
size_t i = 0;
|
||||
do{
|
||||
enmetric(val, 1, buf, 0, 1024, 'i');
|
||||
const int sidx = i / 10;
|
||||
snprintf(gold, sizeof(gold), "%ju.00%ci", goldval, suffixes[sidx]);
|
||||
EXPECT_STREQ(gold, buf);
|
||||
if(UINTMAX_MAX / val < 10){
|
||||
break;
|
||||
}
|
||||
val *= 2;
|
||||
if((goldval *= 2) == 1024){
|
||||
goldval = 1;
|
||||
}
|
||||
}while(++i < sizeof(suffixes) * 10);
|
||||
// If we ran through all our suffixes, that's a problem
|
||||
EXPECT_GT(sizeof(suffixes) * 10, i);
|
||||
}
|
||||
|
||||
TEST(NotcursesPrefix, PowersOfTwoNoDec) {
|
||||
char gold[BPREFIXSTRLEN + 1];
|
||||
char buf[BPREFIXSTRLEN + 1];
|
||||
uintmax_t goldval = 1;
|
||||
uintmax_t val = 1;
|
||||
size_t i = 0;
|
||||
do{
|
||||
enmetric(val, 1, buf, 1, 1024, 'i');
|
||||
const int sidx = i / 10;
|
||||
snprintf(gold, sizeof(gold), "%ju%ci", goldval, suffixes[sidx]);
|
||||
EXPECT_STREQ(gold, buf);
|
||||
if(UINTMAX_MAX / val < 10){
|
||||
break;
|
||||
}
|
||||
val *= 2;
|
||||
if((goldval *= 2) == 1024){
|
||||
goldval = 1;
|
||||
}
|
||||
}while(++i < sizeof(suffixes) * 10);
|
||||
// If we ran through all our suffixes, that's a problem
|
||||
EXPECT_GT(sizeof(suffixes) * 10, i);
|
||||
}
|
||||
|
||||
TEST(NotcursesPrefix, PowersOfTwoAsTens) {
|
||||
char gold[PREFIXSTRLEN + 1];
|
||||
char buf[PREFIXSTRLEN + 1];
|
||||
uintmax_t vfloor = 1;
|
||||
uintmax_t val = 1;
|
||||
size_t i = 0;
|
||||
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
|
||||
do{
|
||||
enmetric(val, 1, buf, 0, 1000, '\0');
|
||||
const int sidx = i / 10;
|
||||
snprintf(gold, sizeof(gold), "%.2f%c",
|
||||
((double)val) / vfloor, suffixes[sidx]);
|
||||
EXPECT_STREQ(gold, buf);
|
||||
if(UINTMAX_MAX / val < 10){
|
||||
break;
|
||||
}
|
||||
val *= 2;
|
||||
if(i % 10 == 9){
|
||||
vfloor *= 1000;
|
||||
}
|
||||
}while(++i < sizeof(suffixes) * 10);
|
||||
// If we ran through all our suffixes, that's a problem
|
||||
EXPECT_GT(sizeof(suffixes) * 10, i);
|
||||
}
|
||||
|
||||
TEST(NotcursesPrefix, PowersOfTenAsTwos) {
|
||||
char gold[BPREFIXSTRLEN + 1];
|
||||
char buf[BPREFIXSTRLEN + 1];
|
||||
uintmax_t vfloor = 1;
|
||||
uintmax_t val = 1;
|
||||
size_t i = 0;
|
||||
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
|
||||
do{
|
||||
enmetric(val, 1, buf, 0, 1024, 'i');
|
||||
const int sidx = i ? (i - 1) / 3 : 0;
|
||||
snprintf(gold, sizeof(gold), "%.2f%ci",
|
||||
((double)val) / vfloor, suffixes[sidx]);
|
||||
EXPECT_STREQ(gold, buf);
|
||||
if(UINTMAX_MAX / val < 10){
|
||||
break;
|
||||
}
|
||||
val *= 10;
|
||||
if(i && i % 3 == 0){
|
||||
vfloor *= 1024;
|
||||
}
|
||||
}while(++i < sizeof(suffixes) * 10);
|
||||
// If we ran through all our suffixes, that's a problem
|
||||
EXPECT_GT(sizeof(suffixes) * 10, i);
|
||||
}
|
||||
|
||||
TEST(NotcursesPrefix, PowersOfTenMinusOne) {
|
||||
char gold[PREFIXSTRLEN + 1];
|
||||
char buf[PREFIXSTRLEN + 1];
|
||||
uintmax_t vfloor = 1;
|
||||
uintmax_t val = 1;
|
||||
size_t i = 0;
|
||||
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
|
||||
do{
|
||||
enmetric(val - 1, 1, buf, 0, 1000, '\0');
|
||||
const int sidx = i ? (i - 1) / 3 : 0;
|
||||
snprintf(gold, sizeof(gold), "%.2f%c",
|
||||
((double)(val - 1)) / vfloor, suffixes[sidx]);
|
||||
EXPECT_STREQ(gold, buf);
|
||||
if(UINTMAX_MAX / val < 10){
|
||||
break;
|
||||
}
|
||||
val *= 10;
|
||||
if(i && i % 3 == 0){
|
||||
vfloor *= 1000;
|
||||
}
|
||||
}while(++i < sizeof(suffixes) * 3);
|
||||
// If we ran through all our suffixes, that's a problem
|
||||
EXPECT_GT(sizeof(suffixes) * 3, i);
|
||||
}
|
||||
|
||||
TEST(NotcursesPrefix, PowersOfTenPlusOne) {
|
||||
char gold[PREFIXSTRLEN + 1];
|
||||
char buf[PREFIXSTRLEN + 1];
|
||||
uintmax_t vfloor = 1;
|
||||
uintmax_t val = 1;
|
||||
size_t i = 0;
|
||||
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
|
||||
do{
|
||||
enmetric(val + 1, 1, buf, 0, 1000, '\0');
|
||||
const int sidx = i / 3;
|
||||
snprintf(gold, sizeof(gold), "%.2f%c",
|
||||
((double)(val + 1)) / vfloor, suffixes[sidx]);
|
||||
EXPECT_STREQ(gold, buf);
|
||||
if(UINTMAX_MAX / val < 10){
|
||||
break;
|
||||
}
|
||||
val *= 10;
|
||||
if(i % 3 == 2){
|
||||
vfloor *= 1000;
|
||||
}
|
||||
}while(++i < sizeof(suffixes) * 3);
|
||||
// If we ran through all our suffixes, that's a problem
|
||||
EXPECT_GT(sizeof(suffixes) * 3, i);
|
||||
}
|
||||
|
||||
TEST(NotcursesPrefix, PowersOfTenMinusOneAsTwos) {
|
||||
char gold[BPREFIXSTRLEN + 1];
|
||||
char buf[BPREFIXSTRLEN + 1];
|
||||
uintmax_t vfloor = 1;
|
||||
uintmax_t val = 1;
|
||||
size_t i = 0;
|
||||
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
|
||||
do{
|
||||
enmetric(val - 1, 1, buf, 0, 1024, 'i');
|
||||
const int sidx = i ? (i - 1) / 3 : 0;
|
||||
snprintf(gold, sizeof(gold), "%.2f%ci",
|
||||
((double)(val - 1)) / vfloor, suffixes[sidx]);
|
||||
EXPECT_STREQ(gold, buf);
|
||||
if(UINTMAX_MAX / val < 10){
|
||||
break;
|
||||
}
|
||||
val *= 10;
|
||||
if(i && i % 3 == 0){
|
||||
vfloor *= 1024;
|
||||
}
|
||||
}while(++i < sizeof(suffixes) * 10);
|
||||
// If we ran through all our suffixes, that's a problem
|
||||
EXPECT_GT(sizeof(suffixes) * 10, i);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user