개인적으로 만드는 라이브러리에서 테스트 할것이 생겨 방치형 돈 단위를 빠르게 구현해서 써야 할 일이 있어서 대강 생각나는대로 구현해 본 내용을 공유합니다.
이런 돈 단위를 구현할 수 있습니다. 아래 링크의 클래스를 복사해서 붙여넣어 사용하면 됩니다. 블로그 하단에 있는 소스코드는 오래 된 내용일 수 있으니 가능한 깃허브에 있는걸 가져다 쓰는게 좋습니다.
GitHub - shlifedev/CAHGamesSDK: Will be Happy Indie Game Develop!
Will be Happy Indie Game Develop! Contribute to shlifedev/CAHGamesSDK development by creating an account on GitHub.
github.com
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text.RegularExpressions;
using UnityEngine;
namespace CAH.GameSystem.BigNumber
{
/// <summary>
/// BigInteger의 단위를 표현할 수 있는 클래스
/// </summary>
public static class BigIntegerManager
{
private static readonly BigInteger _unitSize = 1000;
private static Dictionary<string, BigInteger> _unitsMap = new Dictionary<string, BigInteger>();
private static Dictionary<string, int> _idxMap = new Dictionary<string, int>();
private static readonly List<string> _units = new List<string>();
private static int _unitCapacity = 5;
private static readonly int _asciiA = 65;
private static readonly int _asciiZ = 90;
private static bool isInitialize = false;
private static void UnitInitialize(int capacity)
{
_unitCapacity += capacity;
//Initialize 0~999
_units.Clear();
_unitsMap.Clear();
_idxMap.Clear();
_units.Add("");
_unitsMap.Add("", 0);
_idxMap.Add("", 0);
//capacity만큼 사전생성, capacity가 1인경우 A~Z
//capacity가 2인경우 AA~AZ
//capacity 1마다 ascii 알파벳 26개 생성되는 원리
for (int n = 0; n <= _unitCapacity; n++)
{
for (int i = _asciiA; i <= _asciiZ; i++)
{
string unit = null;
if (n == 0)
unit = ((char) i).ToString();
else
{
var nCount = (float)n / 26;
var nextChar = _asciiA + n - 1;
var fAscii = (char) nextChar;
var tAscii = (char) i;
unit = $"{fAscii}{tAscii}";
}
_units.Add(unit);
_unitsMap.Add(unit, BigInteger.Pow(_unitSize, _units.Count-1));
_idxMap.Add(unit, _units.Count-1);
}
}
isInitialize = true;
}
private static int GetPoint(int value)
{
return (value % 1000) / 100;
}
private static (int value, int idx, int point) GetSize(BigInteger value)
{
//단위를 구하기 위한 값으로 복사
var currentValue = value;
var current = (value / _unitSize) % _unitSize;
var idx = 0;
var lastValue = 0;
// 현재 값이 999(unitSize) 이상인경우 나눠야함.
while (currentValue > _unitSize -1)
{
var predCurrentValue = currentValue / _unitSize;
if (predCurrentValue <= _unitSize - 1)
lastValue = (int)currentValue;
currentValue = predCurrentValue;
idx += 1;
}
var point = GetPoint(lastValue);
var originalValue = currentValue * 1000;
while (_units.Count <= idx)
UnitInitialize(5);
return ((int)currentValue, idx, point);
}
/// <summary>
/// 숫자를 단위로 리턴
/// </summary>
/// <param name="value">값</param>
/// <returns></returns>
public static string GetUnit(BigInteger value)
{
if (isInitialize == false)
UnitInitialize(5);
var sizeStruct = GetSize(value);
return $"{sizeStruct.value}.{sizeStruct.point}{_units[sizeStruct.idx]}";
}
/// <summary>
/// 단위를 숫자로 변경
/// 10A = 10000으로 리턴
/// 1.2A = 1200으로 리턴
/// 소수점 1자리만 지원함
/// </summary>
/// <param name="unit">단위</param>
/// <returns></returns>
public static BigInteger UnitToValue(string unit)
{
if (isInitialize == false)
UnitInitialize(5);
var split = unit.Split('.');
//소수점에 관한 연산 들어감
if (split.Length >= 2)
{
var value = BigInteger.Parse(split[0]);
var point = BigInteger.Parse((Regex.Replace(split[1], "[^0-9]", "")));
var unitStr = Regex.Replace(split[1], "[^A-Z]", "");
if (point == 0) return (_unitsMap[unitStr] * value);
else
{
var unitValue = _unitsMap[unitStr];
return (unitValue * value) + (unitValue/10) * point;
}
}
//비소수 연산 들어감
else
{
var value = BigInteger.Parse((Regex.Replace(unit, "[^0-9]", "")));
var unitStr = Regex.Replace(unit, "[^A-Z]", "");
while (_unitsMap.ContainsKey(unitStr) == false)
UnitInitialize(5);
var result = _unitsMap[unitStr] * value;
if (result == 0)
return int.Parse((unit));
else
return result;
}
}
}
}
김봉식처럼 1.5A = 1500 같은걸 하기위해 소수점 1자리 까지 지원하고
BigIntegerManager.UnitToValue("1.2A")
1.2A는 1200을 리턴
BigIntegerManager.GetUnit(BigInteger.Pow(1000, 702))
1000의 702제곱을 넣었을때에는 1ZZ를 리턴합니다.
//1.2A는 1200을 리턴
BigIntegerManager.UnitToValue("1.2A")
//1000의 702제곱을 넣었을때에는 1ZZ를 리턴합니다.
BigIntegerManager.GetUnit(BigInteger.Pow(1000, 702))
실제로는 ZZ 이상의 숫자도 지원합니다. 하지만 거기까지는 쓰지 않을 것 같아 ZZ이상의 단위 표현은 구현하지 않았습니다. (이게 무슨말이냐면 그 이상을 써도 오류는 없지만 단위가 좀 깨져나올 수 있습니다.) 그리고 너무 높은 숫자를 사용하는경우 내부에서 도는 for문의 루프가 많아지므로 cpu에 많은 부담이 됩니다. 무작정 Update에서 호출하지 마세요!
'프로그래밍 > c#' 카테고리의 다른 글
[C#] BigInteger로 방치형 게임의 A~ZZ 같은 숫자단위 구현 (3) | 2021.08.01 |
---|---|
[c#] imgui.net의 네이티브(cimgui, cimplot..)를 직접 빌드해서 적용하기 (0) | 2021.06.29 |
[c#] imgui.net 한글 출력 안되는 문제 해결 (0) | 2021.06.19 |
[C#] 버그를 줄일 수 있는 '읽기 전용 참조' (0) | 2021.03.06 |