Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</parent>
<groupId>com.tokenphage</groupId>
<artifactId>tokenphage-api</artifactId>
<version>26.08.26</version>
<version>26.10.01</version>
<name>tokenphage-api</name>
<description/>
<url/>
Expand Down
17 changes: 12 additions & 5 deletions src/main/java/com/tokenphage/api/feature/badge/svg/SvgText.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,26 @@ public static String escape(String s) {
/**
* 토큰 수를 읽기 쉬운 단위 문자열로 변환한다.
* <p>
* 1K / 1M / 1B / 1T 단위로 표시하며 소수점 첫째 자리를 유지한다. (예: 1500 → "1.5K")
* 1K / 1M / 1B / 1T 단위로 표시하며 소수점 첫째 자리까지 남기고 나머지는 자른다. (예: 1760 → "1.7K")
* 반올림하지 않으므로 표시값이 실제 값을 넘지 않아, 레벨 임계값 근처에서도 숫자와 레벨이 어긋나지 않는다.
*
* @param tokens 변환할 토큰 수
* @return 단위 변환된 문자열
* @Since 2026-07-16
*/
public static String formatTokens(long tokens) {
return switch (Long.valueOf(tokens)) {
case Long l when l >= 1_000_000_000_000L -> String.format("%.1fT", l / 1_000_000_000_000.0);
case Long l when l >= 1_000_000_000L -> String.format("%.1fB", l / 1_000_000_000.0);
case Long l when l >= 1_000_000L -> String.format("%.1fM", l / 1_000_000.0);
case Long l when l >= 1_000L -> String.format("%.1fK", l / 1_000.0);
case Long l when l >= 1_000_000_000_000L -> truncate(l, 1_000_000_000_000L, "T");
case Long l when l >= 1_000_000_000L -> truncate(l, 1_000_000_000L, "B");
case Long l when l >= 1_000_000L -> truncate(l, 1_000_000L, "M");
case Long l when l >= 1_000L -> truncate(l, 1_000L, "K");
default -> String.valueOf(tokens);
};
}

// 단위로 나눈 값을 소수 첫째 자리에서 자른다. 정수 연산이라 부동소수 오차가 없다.
private static String truncate(long tokens, long unit, String suffix) {
long tenths = tokens / (unit / 10);
return tenths / 10 + "." + tenths % 10 + suffix;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,26 @@ void formatTokens_correctUnit(long tokens, String expected) {
assertThat(SvgText.formatTokens(tokens)).isEqualTo(expected);
}

@ParameterizedTest(name = "[{index}] {0} → {1}")
@DisplayName("소수 첫째 자리에서 자르고 반올림하지 않는다 — 표시값이 실제 값을 넘지 않는다")
@CsvSource({
"1760000000, 1.7B", // 반올림이면 1.8B
"1960000000, 1.9B", // 반올림이면 2.0B
"999960000, 999.9M", // 반올림이면 1000.0M (단위 넘침)
"99960000, 99.9M", // Lv.3 임계(100M) 직전 — 반올림이면 100.0M
"999950, 999.9K", // 반올림이면 1000.0K
"999999999999, 999.9B" // T 단위 직전 — 반올림이면 1000.0B
})
void 토큰표기_반올림경계값_소수첫째자리에서자름(long tokens, String expected) {
// given: 반올림하면 한 자리가 올라가는 경계값

// when
String actual = SvgText.formatTokens(tokens);

// then: 레벨은 원값으로 판정하므로, 표시가 올라가면 임계값 근처에서 숫자와 레벨이 어긋난다
assertThat(actual).isEqualTo(expected);
}

@ParameterizedTest
@DisplayName("XML 특수문자를 이스케이프한다")
@CsvSource({
Expand Down
Loading