Files
XRLib/Assets/Scripts/UVC/Extention/NumberEx.cs
2025-06-04 23:10:11 +09:00

257 lines
9.6 KiB
C#

using System;
namespace UVC.Extension
{
/// <summary>
/// 숫자 관련 확장 메서드를 제공하는 클래스입니다.
/// </summary>
public static class NumberEx
{
/// <summary>
/// 바이트 단위의 숫자를 사람이 읽기 쉬운 KB, MB, GB 등의 형태로 변환합니다.
/// 1024.ToSizeSuffix() => 1KB
/// https://stackoverflow.com/questions/14488796/does-net-provide-an-easy-way-convert-bytes-to-kb-mb-gb-etc
/// </summary>
/// <param name="value">변환할 바이트 크기</param>
/// <param name="decimalPlaces">소수점 자릿수 (기본값: 1)</param>
/// <returns>단위가 포함된 크기 문자열 (예: "1.5 KB")</returns>
/// <exception cref="ArgumentOutOfRangeException">decimalPlaces가 0보다 작은 경우 발생</exception>
/// <example>
/// <code>
/// // 바이트 값을 크기 단위와 함께 표시
/// long fileSize = 1536;
/// string readableSize = fileSize.ToSizeSuffix(); // "1.5 KB" 반환
///
/// // 소수점 2자리까지 표시
/// string preciseSize = fileSize.ToSizeSuffix(2); // "1.50 KB" 반환
///
/// // 더 큰 값 변환
/// long bigFileSize = 1073741824; // 1GB
/// string bigSize = bigFileSize.ToSizeSuffix(); // "1.0 GB" 반환
/// </code>
/// </example>
public static string ToSizeSuffix(this long value, int decimalPlaces = 1)
{
if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces"); }
if (value < 0) { return "-" + ToSizeSuffix(-value, decimalPlaces); }
if (value == 0) { return string.Format("{0:n" + decimalPlaces + "} bytes", 0); }
// mag는 단위를 나타냄: 0=바이트, 1=KB, 2=MB 등
int mag = (int)Math.Log(value, 1024);
// 1L << (mag * 10) == 2 ^ (10 * mag)
// [즉, mag에 해당하는 단위의 바이트 수]
decimal adjustedSize = (decimal)value / (1L << (mag * 10));
// 반올림했을 때 1000 이상이 되는 경우 단위를 한 단계 올림
if (Math.Round(adjustedSize, decimalPlaces) >= 1000)
{
mag += 1;
adjustedSize /= 1024;
}
string[] SizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
return string.Format("{0:n" + decimalPlaces + "} {1}",
adjustedSize,
SizeSuffixes[mag]);
}
/// <summary>
/// 숫자가 지정된 범위 내에 있는지 확인합니다.
/// </summary>
/// <typeparam name="T">비교 가능한 숫자 타입</typeparam>
/// <param name="value">검사할 값</param>
/// <param name="min">최소값 (포함)</param>
/// <param name="max">최대값 (포함)</param>
/// <returns>값이 지정된 범위 내에 있으면 true, 그렇지 않으면 false</returns>
/// <example>
/// <code>
/// int score = 85;
/// bool isInRange = score.IsBetween(0, 100); // true 반환
///
/// float temperature = -5.5f;
/// bool isValidTemp = temperature.IsBetween(-10f, 40f); // true 반환
/// </code>
/// </example>
public static bool IsBetween<T>(this T value, T min, T max) where T : IComparable<T>
{
return value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0;
}
/// <summary>
/// 값을 지정된 최소값과 최대값 사이로 제한합니다.
/// </summary>
/// <typeparam name="T">비교 가능한 숫자 타입</typeparam>
/// <param name="value">제한할 값</param>
/// <param name="min">최소값</param>
/// <param name="max">최대값</param>
/// <returns>최소값과 최대값 사이로 제한된 값</returns>
/// <example>
/// <code>
/// int health = -10;
/// int clampedHealth = health.Clamp(0, 100); // 0 반환
///
/// float speed = 120.5f;
/// float clampedSpeed = speed.Clamp(0f, 100f); // 100f 반환
/// </code>
/// </example>
public static T Clamp<T>(this T value, T min, T max) where T : IComparable<T>
{
if (value.CompareTo(min) < 0) return min;
if (value.CompareTo(max) > 0) return max;
return value;
}
/// <summary>
/// 숫자를 백분율로 변환합니다.
/// </summary>
/// <param name="value">변환할 값 (0.0~1.0)</param>
/// <param name="decimalPlaces">소수점 자릿수</param>
/// <returns>백분율 문자열 (예: "85%")</returns>
/// <example>
/// <code>
/// float progress = 0.85f;
/// string percent = progress.ToPercentString(); // "85.0%" 반환
///
/// double ratio = 0.7525;
/// string precisePercent = ratio.ToPercentString(2); // "75.25%" 반환
/// </code>
/// </example>
public static string ToPercentString(this float value, int decimalPlaces = 1)
{
return string.Format("{0:n" + decimalPlaces + "}%", value * 100);
}
public static string ToPercentString(this double value, int decimalPlaces = 1)
{
return string.Format("{0:n" + decimalPlaces + "}%", value * 100);
}
/// <summary>
/// 초 단위 시간을 시:분:초 형식의 문자열로 변환합니다.
/// </summary>
/// <param name="seconds">초 단위 시간</param>
/// <returns>시:분:초 형식의 문자열</returns>
/// <example>
/// <code>
/// int gameTime = 3725; // 1시간 2분 5초
/// string formattedTime = gameTime.ToTimeString(); // "01:02:05" 반환
///
/// float recordTime = 135.5f; // 2분 15.5초
/// string shortTime = recordTime.ToTimeString(); // "00:02:16" 반환 (반올림됨)
/// </code>
/// </example>
public static string ToTimeString(this int seconds)
{
TimeSpan time = TimeSpan.FromSeconds(seconds);
return time.ToString(@"hh\:mm\:ss");
}
public static string ToTimeString(this float seconds)
{
TimeSpan time = TimeSpan.FromSeconds((double)seconds);
return time.ToString(@"hh\:mm\:ss");
}
/// <summary>
/// 숫자를 서수형 문자열로 변환합니다 (1st, 2nd, 3rd 등).
/// </summary>
/// <param name="number">변환할 숫자</param>
/// <returns>서수형 문자열</returns>
/// <example>
/// <code>
/// int rank = 1;
/// string rankString = rank.ToOrdinal(); // "1st" 반환
///
/// for (int i = 1; i <= 5; i++)
/// {
/// Console.WriteLine(i.ToOrdinal()); // "1st", "2nd", "3rd", "4th", "5th" 출력
/// }
/// </code>
/// </example>
public static string ToOrdinal(this int number)
{
string suffix = "번째";
return number + suffix;
}
/// <summary>
/// 숫자의 각 자리 합을 계산합니다.
/// </summary>
/// <param name="number">계산할 숫자</param>
/// <returns>각 자리 숫자의 합</returns>
/// <example>
/// <code>
/// int number = 12345;
/// int digitSum = number.SumOfDigits(); // 1+2+3+4+5 = 15 반환
/// </code>
/// </example>
public static int SumOfDigits(this int number)
{
int sum = 0;
number = Math.Abs(number);
while (number > 0)
{
sum += number % 10;
number /= 10;
}
return sum;
}
/// <summary>
/// 두 숫자 사이의 선형 보간된 값을 계산합니다.
/// </summary>
/// <param name="start">시작값</param>
/// <param name="end">종료값</param>
/// <param name="t">보간 비율 (0.0 ~ 1.0)</param>
/// <returns>보간된 값</returns>
/// <example>
/// <code>
/// float min = 0f;
/// float max = 100f;
/// float half = min.Lerp(max, 0.5f); // 50f 반환
///
/// // 부드러운 이동 애니메이션 예제
/// float startPosition = 0f;
/// float endPosition = 10f;
/// float t = 0.3f; // 30% 이동
/// float currentPosition = startPosition.Lerp(endPosition, t); // 3f 반환
/// </code>
/// </example>
public static float Lerp(this float start, float end, float t)
{
t = Math.Clamp(t, 0f, 1f);
return start + (end - start) * t;
}
/// <summary>
/// 숫자를 가장 가까운 배수로 반올림합니다.
/// </summary>
/// <param name="value">반올림할 값</param>
/// <param name="factor">배수 단위</param>
/// <returns>가장 가까운 배수로 반올림된 값</returns>
/// <example>
/// <code>
/// int price = 1242;
/// int roundedPrice = price.RoundToNearest(100); // 1200 반환
///
/// float value = 3.7f;
/// float rounded = value.RoundToNearest(0.5f); // 3.5f 반환
/// </code>
/// </example>
public static int RoundToNearest(this int value, int factor)
{
return (int)Math.Round((double)value / factor) * factor;
}
public static float RoundToNearest(this float value, float factor)
{
return (float)Math.Round(value / factor) * factor;
}
}
}