오늘은 어디로 갈까...

숫자(Number) 0x01 본문

낙서

숫자(Number) 0x01

剛宇 2009. 2. 24. 20:18
자, 이제 실제로 유틸리티 클래스를 만들어 보자.
단순히 웹 어플리케이션 개발을 할 경우 실제적으로 많이 쓰이는 것은 문자열을 숫자형으로 변환하는 것이다.
문자열을 입력받아 자바 기본 숫자형으로 변환하는 클래스를 만들어보자
	/**
	 * 문자열을 숫자(short)형으로 변환한다.
	 * 
	 * <pre>
	 * NumberUtils.toShort(null, 0)   = 0
	 * NumberUtils.toShort("NaN", 0)  = 0
	 * NumberUtils.toShort("1234", 0) = 1234
	 * </pre>
	 * 
	 * @param value
	 * @param defaultValue 기본값
	 * @return
	 */
	public static short toShort(String value, short defaultValue) {
		if (value == null) {
			return defaultValue;
		}
		short result = defaultValue;
		try {
			result = Short.parseShort(value);
		} catch (NumberFormatException e) {
			// ignore
		}
		
		return result;
	}
	
	/**
	 * 문자열을 숫자(Short)형으로 변환한다.
	 * 
	 * <pre>
	 * NumberUtils.toShortObject(null)   		= null
	 * NumberUtils.toShortObject("NaN")  		= null
	 * NumberUtils.toShortObject("1234") 		= 1234
	 * </pre>
	 * 
	 * @param value
	 * @return
	 */
	public static Short toShortObject(String value) {
		if (value == null) {
			return null;
		}
		Short result = null;
		try {
			result = new Short(Short.parseShort(value));
		} catch (NumberFormatException e) {
			// ignore
		}	
		return result;
	}

	/**
	 * 문자열을 숫자(Short)형으로 변환한다.
	 * 단, 문자열은 패턴(pattern)과 일치해야한다.
	 * 패턴과 일치하지 않을 경우에는 null을 반환한다.
	 * 
	 * <pre>
	 * NumberUtils.toShortObject(null, *)          = null
	 * NumberUtils.toShortObject("NaN", *)         = null
	 * NumberUtils.toShortObject("1,234", "#,###") = 1234
	 * NumberUtils.toShortObject("1234", "#,###")  = null
	 * NumberUtils.toShortObject("1234", null)     = 1234
	 * </pre>
	 * 
	 * @param value
	 * @param pattern 패턴
	 * @return
	 */
	public static Short toShortObject(String value, String pattern) {
		if (value == null) {
			return null;
		}
		
		Short result = null;
		if (pattern != null) {
			DecimalFormat dFormat = new DecimalFormat(pattern);
			try {
				result = new Short(dFormat.parse(value).shortValue());
				
				if (value.equals(dFormat.format(result)) == false) {
					result = null;
				}
			} catch (ParseException e) {
				// ignore
			}
		} else {
			try {
				result = new Short(Short.parseShort(value));
			} catch (NumberFormatException e) {
				// ignore
			}
		}
		
		return result;
	}
	
	/**
	 * 문자열을 숫자(int)형으로 변환한다.
	 * 
	 * <pre>
	 * NumberUtils.toInteger(null, 0)   = 0
	 * NumberUtils.toInteger("NaN", 0)  = 0
	 * NumberUtils.toInteger("1234", 0) = 1234
	 * </pre>
	 * 
	 * @param value
	 * @param defaultValue 기본값
	 * @return
	 */
	public static int toInteger(String value, int defaultValue) {
		if (value == null) {
			return defaultValue;
		}
		int result = defaultValue;
		try {
			result = Integer.parseInt(value);
		} catch (NumberFormatException e) {
			// ignore
		}
		
		return result;
	}
	
	/**
	 * 문자열을 숫자(Integer)형으로 변환한다.
	 * 
	 * <pre>
	 * NumberUtils.toIntegerObject(null)          = null
	 * NumberUtils.toIntegerObject("NaN")         = null
	 * NumberUtils.toIntegerObject("1234")        = 1234
	 * NumberUtils.toIntegerObject("-2147483649") = null
	 * NumberUtils.toIntegerObject("-2147483648") = -2147483648
	 * NumberUtils.toIntegerObject("2147483647")  = 2147483647
	 * NumberUtils.toIntegerObject("2147483648")  = null
	 * </pre>
	 * 
	 * @param value
	 * @return
	 */
	public static Integer toIntegerObject(String value) {
		if (value == null) {
			return null;
		}
		Integer result = null;
		try {
			result = new Integer(Integer.parseInt(value));
		} catch (NumberFormatException e) {
			// ignore
		}	
		return result;
	}

	/**
	 * 문자열을 숫자(Integer)형으로 변환한다.
	 * 단, 문자열은 패턴(pattern)과 일치해야한다.
	 * 패턴과 일치하지 않을 경우에는 null을 반환한다.
	 * 
	 * <pre>
	 * NumberUtils.toIntegerObject(null, *)          = null
	 * NumberUtils.toIntegerObject("NaN", *)         = null
	 * NumberUtils.toIntegerObject("1,234", "#,###") = 1234
	 * NumberUtils.toIntegerObject("1234", "#,###")  = null
	 * NumberUtils.toIntegerObject("1234", null)     = 1234
	 * </pre>
	 * 
	 * @param value
	 * @param pattern 패턴
	 * @return
	 */
	public static Integer toIntegerObject(String value, String pattern) {
		if (value == null) {
			return null;
		}
		
		Integer result = null;
		if (pattern != null) {
			DecimalFormat dFormat = new DecimalFormat(pattern);
			try {
				result = new Integer(dFormat.parse(value).intValue());
				
				if (value.equals(dFormat.format(result)) == false) {
					result = null;
				}
			} catch (ParseException e) {
				// ignore
			}
		} else {
			try {
				result = new Integer(Integer.parseInt(value));
			} catch (NumberFormatException e) {
				// ignore
			}
		}
		
		return result;
	}
	
	/**
	 * 문자열을 숫자(long)형으로 변환한다.
	 * 
	 * <pre>
	 * NumberUtils.toLong(null, 0)   = 0
	 * NumberUtils.toLong("NaN", 0)  = 0
	 * NumberUtils.toLong("1234", 0) = 1234
	 * </pre>
	 * 
	 * @param value
	 * @param defaultValue 기본값
	 * @return
	 */
	public static long toLong(String value, long defaultValue) {
		long result = defaultValue;

		try {
			result = Long.parseLong(value);
		} catch (NumberFormatException e) {
			// ignore
		}
		
		return result;
	}
	
	/**
	 * 문자열을 숫자(Long)형으로 변환한다.
	 * 
	 * <pre>
	 * NumberUtils.toLongObject(null)   = null
	 * NumberUtils.toLongObject("NaN")  = null
	 * NumberUtils.toLongObject("1234") = 1234
	 * </pre>
	 * 
	 * @param value
	 * @return
	 */
	public static Long toLongObject(String value) {
		Long result = null;
		try {
			result = new Long(Long.parseLong(value));
		} catch (NumberFormatException e) {
			// ignore
		}	
		return result;
	}
	
	/**
	 * 문자열을 숫자(Long)형으로 변환한다.
	 * 단, 문자열은 패턴(pattern)과 일치해야한다.
	 * 패턴과 일치하지 않을 경우에는 null을 반환한다.
	 * 
	 * <pre>
	 * NumberUtils.toLongObject(null, *)            = null
	 * NumberUtils.toLongObject("NaN", *)           = null
	 * NumberUtils.toLongObject("1,234", "#,###")   = 1234
	 * NumberUtils.toLongObject("1234", "#,###")    = null
	 * NumberUtils.toLongObject("1234", null)       = 1234
	 * </pre>
	 * 
	 * @param value
	 * @param pattern 패턴
	 * @return
	 */
	public static Long toLongObject(String value, String pattern) {
		if (value == null) {
			return null;
		}
		
		Long result = null;
		if (pattern != null) {
			DecimalFormat dFormat = new DecimalFormat(pattern);
			try {
				result = new Long(dFormat.parse(value).longValue());
				
				if (value.equals(dFormat.format(result)) == false) {
					result = null;
				}
			} catch (ParseException e) {
				// ignore
			}
		} else {
			try {
				result = new Long(Long.parseLong(value));
			} catch (NumberFormatException e) {
				// ignore
			}
		}
		
		return result;
	}
	
	/**
	 * 문자열을 숫자(float)형으로 변환한다.
	 * 
	 * <pre>
	 * NumberUtils.toFloat(null, 0)     = 0.0
	 * NumberUtils.toFloat("NaN", 0)    = 0.0
	 * NumberUtils.toFloat("1234.5", 0) = 1234.5
	 * </pre>
	 * 
	 * @param value
	 * @param defaultValue 기본값
	 * @return
	 */
	public static float toFloat(String value, float defaultValue) {
		if (value == null) {
			return defaultValue;
		}
		try {
			return Float.parseFloat(value);
		} catch (NumberFormatException e) {
			return defaultValue;
		}
	}

	/**
	 * 문자열을 숫자(Float)형으로 변환한다.
	 * 
	 * <pre>
	 * NumberUtils.toFloatObject(null)     = null
	 * NumberUtils.toFloatObject("AAA")    = null
	 * NumberUtils.toFloatObject("NaN")    = Float.NAN
	 * NumberUtils.toFloatObject("1234.5") = 1234.5
	 * </pre>
	 * 
	 * @param value
	 * @return
	 */
	public static Float toFloatObject(String value) {
		if (value == null) {
			return null;
		}
		try {
			return new Float(Float.parseFloat(value));
		} catch (NumberFormatException e) {
			return null;
		}	
	}
	

	/**
	 * 문자열을 숫자(Float)형으로 변환한다.
	 * 단, 문자열은 패턴(pattern)과 일치해야한다.
	 * 패턴과 일치하지 않을 경우에는 null을 반환한다.
	 * 
	 * <pre>
	 * NumberUtils.toFloatObject(null, *)              = null
	 * NumberUtils.toFloatObject("AAA", *)             = null
	 * NumberUtils.toFloatObject("NaN", *)             = Float.NAN
	 * NumberUtils.toFloatObject("1,234", "#,###.#")   = 1234.0
	 * NumberUtils.toFloatObject("1,234.5", "#,###.#") = 1234.5
	 * NumberUtils.toFloatObject("1234.5", "#,###")    = null
	 * NumberUtils.toFloatObject("1234.5", null)       = 1234.5
	 * </pre>
	 * 
	 * @param value
	 * @param pattern 패턴
	 * @return
	 */
	public static Float toFloatObject(String value, String pattern) {
		if (value == null) {
			return null;
		}
		
		Float result = null;
		if (pattern != null) {
			DecimalFormat dFormat = new DecimalFormat(pattern);
			try {
				result = new Float(dFormat.parse(value).floatValue());
				
				if (value.equals(dFormat.format(result)) == false) {
					result = null;
				}
			} catch (ParseException e) {
				// ignore
			}
		} else {
			try {
				result = new Float(Float.parseFloat(value));
			} catch (NumberFormatException e) {
				// ignore
			}
		}
		
		return result;
	}
	
	/**
	 * 문자열을 숫자(double)형으로 변환한다.
	 * 
	 * <pre>
	 * NumberUtils.toDouble(null, 0)   = 0.0
	 * NumberUtils.toDouble("AAA", 0)  = 0.0
	 * NumberUtils.toDouble("NaN", 0)  = 0.0
	 * NumberUtils.toDouble("1234.5", 0) = 1234.5
	 * </pre>
	 * 
	 * @param value
	 * @param defaultValue 기본값
	 * @return
	 */
	public static double toDouble(String value, double defaultValue) {
		if (value == null) {
			return defaultValue;
		}

		try {
			return Double.parseDouble(value);
		} catch (NumberFormatException e) {
			return defaultValue;
		}
	}

	/**
	 * 문자열을 숫자(Double)형으로 변환한다.
	 * 
	 * <pre>
	 * NumberUtils.toDoubleObject(null)     = null
	 * NumberUtils.toDoubleObject("NaN")    = null
	 * NumberUtils.toDoubleObject("1234.5") = 1234.5
	 * </pre>
	 * 
	 * @param value
	 * @return
	 */
	public static Double toDoubleObject(String value) {
		if (value == null) {
			return null;
		}
		try {
			return new Double(Double.parseDouble(value));
		} catch (NumberFormatException e) {
			return null;
		}	
	}
	

	/**
	 * 문자열을 숫자(Double)형으로 변환한다.
	 * 단, 문자열은 패턴(pattern)과 일치해야한다.
	 * 패턴과 일치하지 않을 경우에는 null을 반환한다.
	 * 
	 * <pre>
	 * NumberUtils.toDoubleObject(null, *)                  = null
	 * NumberUtils.toDoubleObject("NaN", *)                 = null
	 * NumberUtils.toDoubleObject("1,234", "#,###.#")       = 1234.0
	 * NumberUtils.toDoubleObject("1,234.5", "#,###.#")     = 1234.5
	 * NumberUtils.toDoubleObject("1234.5", "#,###")        = null
	 * NumberUtils.toDoubleObject("1234.5", null)           = 1234.5
	 * </pre>
	 * 
	 * @param value
	 * @param pattern 패턴
	 * @return
	 */
	public static Double toDoubleObject(String value, String pattern) {
		if (value == null) {
			return null;
		}
		
		Double result = null;
		if (pattern != null) {
			DecimalFormat dFormat = new DecimalFormat(pattern);
			try {
				result = new Double(dFormat.parse(value).doubleValue());
				
				if (value.equals(dFormat.format(result)) == false) {
					result = null;
				}
			} catch (ParseException e) {
				// ignore
			}
		} else {
			try {
				result = new Double(Double.parseDouble(value));
			} catch (NumberFormatException e) {
				// ignore
			}
		}
		
		return result;
	}
단순히 null 체크나, 변환 실패할 경우만 체크하고 나머지는 자바에서 기본적으로 제공해주는 parserXXX를 사용해서 어려운 부분이 없으니, 설명은 생략하도록하겠다.

 별로 쓸 일은 없겠지만 unsigned int 형을 다뤄보자. 자바에는 unsigned 형이 없다. 그래서 int형을 unsigned int 형 처럼 사용할려면 i & 0xfffffffL; 연산을 이용해서 long형에 담으면 부호없는 int 형 처럼 보인다.
	/**
	 * 입력한 i값을 unsigned int로 변환한다.
	 * 자바는 unsinged int가 없기때문에 long형으로 반환한다.
	 * 
	 * <pre>
	 * NumberUtils.unsignedInt(1) 	= 1
	 * NumberUtils.unsignedInt(0) 	= 0
	 * NumberUtils.unsignedInt(-1) 	= 4294967295
	 * </pre>
	 * 
	 * @param i signed int
	 * @return unsigned int
	 */
	public static long unsignedInt(int i) {
		return i & 0xFFFFFFFFL;
	}

테스트 케이스를 만들어보자.
	@Test
	public void testUnsignedInt() {
		Assert.assertEquals(NumberUtils.unsignedInt(0xFFFFFFF0), 0xFFFFFFF0L);
		Assert.assertEquals(NumberUtils.unsignedInt(0xFFFFFFFF), 0xFFFFFFFFL);
	}

0xFFFFFFFF는 int형일 경우 -1이다. 근데 잘 보면 비교값이 0xFFFFFFFFL 이다. 'L'이란 문자가 붙는다. 이 'L'은 long 타입의 숫자란 뜻이다. 그래서 0xFFFFFFFFL = 4294967295임으로 원하는 결과가 나오게 되는것이다.

이번에 비트(bit) 연산을 한번 해볼까.
'&'(AND) 연산은 두 값이 다 1일때만 1을, 아니면 0을 나타낸다.
 예) 1011 & 1101 = 1001
'|'(OR) 연산은 두 값중 하나라도 1일때 1을, 둘다 0이면 면 0을 나타낸다.
 예) 1011 & 1101 = 1111
shift 연산
'<<' 은 비트단위만큼 왼쪽으로 이동을 한다는 뜻이다. 오른쪽은 0으로 채운다.
 예) 10111010 << 4 = 10100000
'>>' 은 비트단위만큼 오른쪽으로 이동을 한다는 뜻이다. 만일 최상위 비트가 1인 경우 왼쪽을 1로 채우고 아닐 경우 0으로 채운다.
 예) 10111010 >> 4 = 11111011
     00111010 >> 4 = 00000011
'>>>'은 비트단위만큼 오른쪽으로 이동을 한다는 뜻이다. 단, 무조건 왼쪽을 0으로 채운다.
 예) 10111010 >>> 4 = 00001011
     00111010 >>> 4 = 00000011
'~'은 보수로서 0을 1로, 1을 0으로 바꾼다.
자바에서는 shift 연산은 지원하는데, rotate 연산은 지원하지 않는다.(아마도..) 그래서 위의 연산을 가지고 roate를 구현해보자
덤으로 long형의 앞 워드(word)와 뒷 워드(word)를 추출하는것도 만들어보자.
	/**
	 * <p>
	 * long 형의 앞 워드(word=4byte)를 추출한다.
	 * </p>
	 * 
	 * @param l
	 * @return
	 */
	public static int extractW0(long l) {
		return (int) (l >> 32);
	}

	/**
	 * <p>
	 * long 형의 뒤 워드(word=4byte)를 추출한다.
	 * </p>
	 * 
	 * @param l
	 * @return
	 */
	public static int extractW1(long l) {
		return (int) l;
	}

	/**
	 * <p>
	 * 지정한 거리만큼 왼쪽으로 로테이트(rotate)한다.
	 * </p>
	 * 
	 * @param i
	 * @param distance
	 *            로테이트 할 비트 수
	 * @return
	 */
	public static int rotateLeft(int i, int distance) {
		return (i << distance) | (i >>> -distance);
	}

	/**
	 * <p>
	 * 지정한 거리만큼 오른쪽으로 로테이트(rotate)한다.
	 * </p>
	 * 
	 * @param i
	 * @param distance
	 *            로테이트 할 비트 수
	 * @return
	 */
	public static int rotateRight(int i, int distance) {
		return (i >>> distance) | (i << -distance);
	}

	/**
	 * <p>
	 * 지정한 거리만큼 왼쪽으로 로테이트(rotate)한다.
	 * </p>
	 * 
	 * 
	 * @param i
	 * @param distance
	 *            로테이트 할 비트 수
	 * @return
	 */
	public static long rotateLeft(long i, int distance) {
		return (i << distance) | (i >>> -distance);
	}

	/**
	 * <p>
	 * 지정한 거리만큼 오른쪽으로 로테이트(rotate)한다.
	 * </p>
	 * 
	 * @param i
	 * @param distance
	 *            로테이트 할 비트 수
	 * @return
	 */
	public static long rotateRight(long i, int distance) {
		return (i >>> distance) | (i << -distance);
	}

	@Test
	public void testExtractW0() {
		Assert.assertEquals(NumberUtils.extractW0(0xFFFFFFFF00000000L), 0xFFFFFFFF);
		Assert.assertEquals(NumberUtils.extractW0(0x00000001FFFFFFFFL), 0x00000001);
		Assert.assertEquals(NumberUtils.extractW0(0x00000000FFFFFFFFL), 0x00000000);
	}

	@Test
	public void testExtractW1() {
		Assert.assertEquals(NumberUtils.extractW1(0x00000000FFFFFFFFL), 0xFFFFFFFF);
		Assert.assertEquals(NumberUtils.extractW1(0xFFFFFFFF00000000L), 0x00000000);
		Assert.assertEquals(NumberUtils.extractW1(0xFFFFFFFF00000001L), 0x00000001);
	}

	@Test
	public void testRotateLeftIntInt() {
		Assert.assertEquals(NumberUtils.rotateLeft(0xF2AB34CD, 4), 0x2AB34CDF);
		Assert.assertEquals(NumberUtils.rotateLeft(0xF2AB34CD, 8), 0xAB34CDF2);
		Assert.assertEquals(NumberUtils.rotateLeft(0xF2AB34CD, 16), 0x34CDF2AB);
		Assert.assertEquals(NumberUtils.rotateLeft(0x02AB34CD, 4), 0x2AB34CD0);
	}

	@Test
	public void testRotateRightIntInt() {
		Assert.assertEquals(NumberUtils.rotateRight(0xF2AB34CD, 4), 0xDF2AB34C);
		Assert.assertEquals(NumberUtils.rotateRight(0xF2AB34CD, 8), 0xCDF2AB34);
		Assert.assertEquals(NumberUtils.rotateRight(0xF2AB34CD, 16), 0x34CDF2AB);
		Assert.assertEquals(NumberUtils.rotateRight(0x02AB34CD, 4), 0xD02AB34C);
	}

	@Test
	public void testRotateLeftLongInt() {
		Assert.assertEquals(NumberUtils.rotateLeft(0xF2AB34CD00000000L, 4), 0x2AB34CD00000000FL);
		Assert.assertEquals(NumberUtils.rotateLeft(0xF2AB34CD00000000L, 8), 0xAB34CD00000000F2L);
		Assert.assertEquals(NumberUtils.rotateLeft(0xF2AB34CD00000000L, 16), 0x34CD00000000F2ABL);
		Assert.assertEquals(NumberUtils.rotateLeft(0x02AB34CD00000000L, 4), 0x2AB34CD000000000L);
	}

	@Test
	public void testRotateRightLongInt() {
		Assert.assertEquals(NumberUtils.rotateRight(0xF2AB34CD00000000L, 4), 0x0F2AB34CD0000000L);
		Assert.assertEquals(NumberUtils.rotateRight(0xF2AB34CD00000000L, 8), 0x00F2AB34CD000000L);
		Assert.assertEquals(NumberUtils.rotateRight(0xF2AB34CD00000000L, 16), 0x0000F2AB34CD0000L);
		Assert.assertEquals(NumberUtils.rotateRight(0x02AB34CD00000000L, 4), 0x002AB34CD0000000L);
	}