실수에서 정수형변환시 값손실
아래는 실수를 문자열로 변환해주는 함수 인데 허점이 있는거 같습니다.
잘 작동할도 있고 오작동할때도 있는데
오작동 하면
30.8을 변환하면 30.7999999 가 되버립니다.
원인을 파악하기 위해 로그를 남기던중
형변환시 값손실이(14.0 -> 13) 일어나고 그로인해
정상작동시에는 while문을 몇번 돌지 않지만
오작동시에는 do while문을 과도하게 많이 돌게 되는것을 발견했습니다.
어떻게 해결해야 할까요..?
size_t utils_floatToText(double data, uint8_t * string, size_t length) { size_t intLength; size_t decLength; int64_t intPart; double decPart; if (data <= (double)INT64_MIN || data >= (double)INT64_MAX) return 0; intPart = (int64_t)data; decPart = data - intPart; if (decPart < 0) { decPart = 1 - decPart; } else { decPart = 1 + decPart; } if (decPart <= 1 + FLT_EPSILON) { decPart = 0; } if (intPart == 0 && data < 0) { // deal with numbers between -1 and 0 if (length < 4) return 0; // "-0.n" string[0] = '-'; string[1] = '0'; intLength = 2; } else { intLength = utils_intToText(intPart, string, length); if (intLength == 0) return 0; } decLength = 0; if (decPart >= FLT_EPSILON) { int i; double noiseFloor; if (intLength >= length - 1) return 0; i = 0; noiseFloor = FLT_EPSILON; do { decPart *= 10; noiseFloor *= 10; fprintf(stderr,"decPart = %f , (int64)dec Part = %d,noiseFloor = %f \n",decPart,(int64_t)decPart,noiseFloor); i++; } while (decPart - (int64_t)decPart > noiseFloor); decLength = utils_intToText(decPart, string + intLength, length - intLength); if (decLength <= 1) return 0; // replace the leading 1 with a dot string[intLength] = '.'; } fprintf(stderr,"decPart = %f , convert = %s \n",decPart,string + intLength); return intLength + decLength; }
아래는 실행 로그 입니다.
dataP->value.asFloat : 31.400000
decPart = 14.000000 , (int64)dec = 13 , noiseFloor = 0.000001
decPart = 140.000000 , (int64)dec = 139 , noiseFloor = 0.000012
decPart = 1400.000000 , (int64)dec = 1399 , noiseFloor = 0.000119
decPart = 14000.000000 , (int64)dec = 13999 , noiseFloor = 0.001192
decPart = 140000.000000 , (int64)dec = 139999 , noiseFloor = 0.011921
decPart = 1400000.000000 , (int64)dec = 1399999 , noiseFloor = 0.119209
decPart = 14000000.000000 , (int64)dec = 13999999 , noiseFloor = 1.192093
decPart = 14000000.000000 , convert = .3999999 31.400000
utils_float64ToPlainText 31.400000 31.3999999 �
expernet data
report change!
dataP->value.asFloat : 24.800000
decPart = 18.000000 , (int64)dec = 18 , noiseFloor = 0.000001
decPart = 18.000000 , convert = .8 24.800000
utils_float64ToPlainText 24.800000 24.8%
report change!
이 문제는 게시판에 많이 올라온 문제이니 검색해
이 문제는 게시판에 많이 올라온 문제이니 검색해 보세요.
기본적으로 분모에 2 이외의 다른 수가 나오는 소수는 2진수에서 무한 소수이기 때문에 컴퓨터에 정확히 입력되지가 않습니다.
30.8이 30.7999999로 변환되는 것은 오히려 프로그램이 올바르게 동작했다는 거죠.
실수를 비교에 사용하는 것은 매우 위험합니다. 어느 정도의 tolerance 를 허용하여 해결될 수 있는 경우가 아니면 쓰지 않는게 좋습니다.
부동소수점 표현 양식상 어쩔 수 없습니다.
부동소수점 표현 양식상 어쩔 수 없습니다.
부동소수점 연산은 근사치로 대충 때려맞춰도 되는 곳에만 서야 합니다.
소수점 이하 몇자리까지 정확한 연산이 필요하다면 원값에 x10, x100한 정수 쓰고, 표시할때 자릿수별로 나눠주는게 좋습니다.
Written By the Black Knight of Destruction
댓글 달기