generated from nhcarrigan/template
update formatNumber
This commit is contained in:
@@ -462,12 +462,12 @@ interface GameContextValue {
|
|||||||
/**
|
/**
|
||||||
* Format a number using the player's chosen notation style.
|
* Format a number using the player's chosen notation style.
|
||||||
*/
|
*/
|
||||||
formatNumber: (value: number)=> string;
|
formatNumber: (value: number, format?: NumberFormat)=> string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a whole-number value without decimal places.
|
* Format a whole-number value without decimal places.
|
||||||
*/
|
*/
|
||||||
formatInteger: (value: number, format: NumberFormat)=> string;
|
formatInteger: (value: number, format?: NumberFormat)=> string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Buy a prestige upgrade from the runestone shop.
|
* Buy a prestige upgrade from the runestone shop.
|
||||||
|
|||||||
@@ -57,22 +57,32 @@ const getLetterSuffix = (index: number): string => {
|
|||||||
/**
|
/**
|
||||||
* Formats a number with a named or letter-based suffix.
|
* Formats a number with a named or letter-based suffix.
|
||||||
* @param value - The number to format.
|
* @param value - The number to format.
|
||||||
|
* @param round - Display zero numbers after a decimal point.
|
||||||
* @returns The formatted string with suffix.
|
* @returns The formatted string with suffix.
|
||||||
*/
|
*/
|
||||||
const formatSuffix = (value: number): string => {
|
const formatSuffix = (value: number, round = false): string => {
|
||||||
if (value >= Math.pow(10, letterBaseExp)) {
|
if (value >= Math.pow(10, letterBaseExp)) {
|
||||||
const exp = Math.floor(Math.log10(value));
|
const exp = Math.floor(Math.log10(value));
|
||||||
const stepsAboveBase = Math.floor((exp - letterBaseExp) / 3);
|
const stepsAboveBase = Math.floor((exp - letterBaseExp) / 3);
|
||||||
const steps = stepsAboveBase * 3;
|
const steps = stepsAboveBase * 3;
|
||||||
const divisorExp = letterBaseExp + steps;
|
const divisorExp = letterBaseExp + steps;
|
||||||
const divisor = Math.pow(10, divisorExp);
|
const divisor = Math.pow(10, divisorExp);
|
||||||
return `${(value / divisor).toFixed(2)}${getLetterSuffix(stepsAboveBase)}`;
|
return `${round
|
||||||
|
? String(Math.round(value / divisor))
|
||||||
|
: (value / divisor).toFixed(2)}${getLetterSuffix(stepsAboveBase)}`;
|
||||||
}
|
}
|
||||||
for (const { threshold, suffix } of namedSuffixes) {
|
for (const { threshold, suffix } of namedSuffixes) {
|
||||||
if (value >= threshold) {
|
if (value >= threshold) {
|
||||||
return `${(value / threshold).toFixed(2)}${suffix}`;
|
return `${round
|
||||||
|
? String(Math.floor(value / threshold))
|
||||||
|
: (value / threshold).toFixed(2)}${suffix}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (round) {
|
||||||
|
return String(Math.floor(value));
|
||||||
|
}
|
||||||
|
|
||||||
return value < 1
|
return value < 1
|
||||||
? value.toFixed(2)
|
? value.toFixed(2)
|
||||||
: value.toFixed(1);
|
: value.toFixed(1);
|
||||||
@@ -82,12 +92,18 @@ const formatSuffix = (value: number): string => {
|
|||||||
* Formats a number in scientific notation: e.g. 1.23e15.
|
* Formats a number in scientific notation: e.g. 1.23e15.
|
||||||
* Falls back to K/M/B/T style below 1 million.
|
* Falls back to K/M/B/T style below 1 million.
|
||||||
* @param value - The number to format.
|
* @param value - The number to format.
|
||||||
|
* @param round - Display zero numbers after a decimal point.
|
||||||
* @returns The formatted string in scientific notation.
|
* @returns The formatted string in scientific notation.
|
||||||
*/
|
*/
|
||||||
const formatScientific = (value: number): string => {
|
const formatScientific = (value: number, round = false): string => {
|
||||||
if (value < 1e6) {
|
if (value < 1e6) {
|
||||||
return formatSuffix(value);
|
return formatSuffix(value, round);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (round) {
|
||||||
|
return value.toExponential(0).replace("e+", "e");
|
||||||
|
}
|
||||||
|
|
||||||
// ToExponential handles all magnitudes JS can represent (up to ~1.8e308)
|
// ToExponential handles all magnitudes JS can represent (up to ~1.8e308)
|
||||||
return value.toExponential(2).replace("e+", "e");
|
return value.toExponential(2).replace("e+", "e");
|
||||||
};
|
};
|
||||||
@@ -96,16 +112,19 @@ const formatScientific = (value: number): string => {
|
|||||||
* Formats a number in engineering notation (exponent always a multiple of 3):
|
* Formats a number in engineering notation (exponent always a multiple of 3):
|
||||||
* e.g. 12.35E12, 1.23E300. Falls back to K/M/B/T style below 1 million.
|
* e.g. 12.35E12, 1.23E300. Falls back to K/M/B/T style below 1 million.
|
||||||
* @param value - The number to format.
|
* @param value - The number to format.
|
||||||
|
* @param round - Display zero numbers after a decimal point.
|
||||||
* @returns The formatted string in engineering notation.
|
* @returns The formatted string in engineering notation.
|
||||||
*/
|
*/
|
||||||
const formatEngineering = (value: number): string => {
|
const formatEngineering = (value: number, round = false): string => {
|
||||||
if (value < 1e6) {
|
if (value < 1e6) {
|
||||||
return formatSuffix(value);
|
return formatSuffix(value, round);
|
||||||
}
|
}
|
||||||
const exp = Math.floor(Math.log10(value));
|
const exp = Math.floor(Math.log10(value));
|
||||||
const engExp = Math.floor(exp / 3) * 3;
|
const engExp = Math.floor(exp / 3) * 3;
|
||||||
const mantissa = value / Math.pow(10, engExp);
|
const mantissa = value / Math.pow(10, engExp);
|
||||||
return `${mantissa.toFixed(2)}E${String(engExp)}`;
|
return `${round
|
||||||
|
? String(Math.round(mantissa))
|
||||||
|
: mantissa.toFixed(2)}E${String(engExp)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,37 +143,19 @@ const formatInteger = (value: number,
|
|||||||
return `-${formatInteger(-value)}`;
|
return `-${formatInteger(-value)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const flooredValue = Math.floor(value);
|
const roundedValue = Math.floor(value);
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case "scientific":
|
case "scientific":
|
||||||
return formatScientific(flooredValue);
|
return formatScientific(roundedValue, true);
|
||||||
case "engineering":
|
case "engineering":
|
||||||
return formatEngineering(flooredValue);
|
return formatEngineering(value, true);
|
||||||
case "suffix":
|
case "suffix":
|
||||||
return formatSuffix(flooredValue);
|
return formatSuffix(value, true);
|
||||||
default: {
|
default: {
|
||||||
/* V8 ignore next -- @preserve */
|
/* V8 ignore next -- @preserve */
|
||||||
return formatSuffix(flooredValue);
|
return formatSuffix(value, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*If (value >= Math.pow(10, letterBaseExp)) {
|
|
||||||
* const exp = Math.floor(Math.log10(value));
|
|
||||||
* const stepsAboveBase = Math.floor((exp - letterBaseExp) / 3);
|
|
||||||
* const steps = stepsAboveBase * 3;
|
|
||||||
* const divisorExp = letterBaseExp + steps;
|
|
||||||
* const divisor = Math.pow(10, divisorExp);
|
|
||||||
* return `${String(Math.round(value / divisor))}${getLetterSuffix(stepsAboveBase)}`;
|
|
||||||
*}
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*for (const { threshold, suffix } of namedSuffixes) {
|
|
||||||
* if (value >= threshold) {
|
|
||||||
* return `${String(Math.floor(value / threshold))}${suffix}`;
|
|
||||||
* }
|
|
||||||
*}
|
|
||||||
*return String(Math.floor(value));
|
|
||||||
*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -197,108 +197,42 @@ describe("formatInteger", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("suffix format (default)", () => {
|
|
||||||
it("should format small numbers with one decimal place", () => {
|
|
||||||
expect(formatInteger(999)).toBe("999.0");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format thousands with K suffix", () => {
|
|
||||||
expect(formatInteger(1000)).toBe("1.00K");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format millions with M suffix", () => {
|
|
||||||
expect(formatInteger(1_000_000)).toBe("1.00M");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format billions with B suffix", () => {
|
|
||||||
expect(formatInteger(1_000_000_000)).toBe("1.00B");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format trillions with T suffix", () => {
|
|
||||||
expect(formatInteger(1e12)).toBe("1.00T");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format quadrillions with Qa suffix", () => {
|
|
||||||
expect(formatInteger(1e15)).toBe("1.00Qa");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format quintillions with Qi suffix", () => {
|
|
||||||
expect(formatInteger(1e18)).toBe("1.00Qi");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format sextillions with Sx suffix", () => {
|
|
||||||
expect(formatInteger(1e21)).toBe("1.00Sx");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format septillions with Sp suffix", () => {
|
|
||||||
expect(formatInteger(1e24)).toBe("1.00Sp");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format octillions with Oc suffix", () => {
|
|
||||||
expect(formatInteger(1e27)).toBe("1.00Oc");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format nonillions with No suffix", () => {
|
|
||||||
expect(formatInteger(1e30)).toBe("1.00No");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format decillions with Dc suffix", () => {
|
|
||||||
expect(formatInteger(1e33)).toBe("1.00Dc");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format values >= 1e36 with letter suffix 'a'", () => {
|
|
||||||
expect(formatInteger(1e36)).toBe("1.00a");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format values >= 1e39 with letter suffix 'b'", () => {
|
|
||||||
expect(formatInteger(1e39)).toBe("1.00b");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format values at 26th letter step with 'z'", () => {
|
|
||||||
expect(formatInteger(1e36 * Math.pow(10, 25 * 3))).toBe("1.00z");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format values at 27th letter step with 'aa'", () => {
|
|
||||||
expect(formatInteger(1e36 * Math.pow(10, 26 * 3))).toBe("1.00aa");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("scientific format", () => {
|
describe("scientific format", () => {
|
||||||
it("should fall back to suffix format below 1e6", () => {
|
it("should fall back to suffix format below 1e6", () => {
|
||||||
expect(formatInteger(500, "scientific")).toBe("500.0");
|
expect(formatInteger(500, "scientific")).toBe("500");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should format values >= 1e6 in scientific notation", () => {
|
it("should format values >= 1e6 in scientific notation", () => {
|
||||||
expect(formatInteger(1_230_000, "scientific")).toBe("1.23e6");
|
expect(formatInteger(1_230_000, "scientific")).toBe("1e6");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should format large values in scientific notation", () => {
|
it("should format large values in scientific notation", () => {
|
||||||
expect(formatInteger(1e18, "scientific")).toBe("1.00e18");
|
expect(formatInteger(1e18, "scientific")).toBe("1e18");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("engineering format", () => {
|
describe("engineering format", () => {
|
||||||
it("should fall back to suffix format below 1e6", () => {
|
it("should fall back to suffix format below 1e6", () => {
|
||||||
expect(formatInteger(500, "engineering")).toBe("500.0");
|
expect(formatInteger(500, "engineering")).toBe("500");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should format values >= 1e6 with exponent multiple of 3", () => {
|
it("should format values >= 1e6 with exponent multiple of 3", () => {
|
||||||
expect(formatInteger(1_230_000, "engineering")).toBe("1.23E6");
|
expect(formatInteger(1_230_000, "engineering")).toBe("1E6");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should format 1e9 correctly in engineering notation", () => {
|
it("should format 1e9 correctly in engineering notation", () => {
|
||||||
expect(formatInteger(1e9, "engineering")).toBe("1.00E9");
|
expect(formatInteger(1e9, "engineering")).toBe("1E9");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should format 12350000 correctly in engineering notation", () => {
|
it("should format 12350000 correctly in engineering notation", () => {
|
||||||
expect(formatInteger(12_350_000, "engineering")).toBe("12.35E6");
|
expect(formatInteger(12_350_000, "engineering")).toBe("12E6");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("unknown format (default branch)", () => {
|
describe("unknown format (default branch)", () => {
|
||||||
it("should fall back to suffix format for an unrecognised format string", () => {
|
it("should fall back to suffix format for an unrecognised format string", () => {
|
||||||
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Testing unreachable default branch */
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Testing unreachable default branch */
|
||||||
expect(formatInteger(1000, "unknown" as never)).toBe("1.00K");
|
expect(formatInteger(1000, "unknown" as never)).toBe("1K");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user