실수에서 정수형변환시 값손실

unuseid의 이미지

아래는 실수를 문자열로 변환해주는 함수 인데 허점이 있는거 같습니다.

잘 작동할도 있고 오작동할때도 있는데
오작동 하면
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!

ifree의 이미지

이 문제는 게시판에 많이 올라온 문제이니 검색해 보세요.
기본적으로 분모에 2 이외의 다른 수가 나오는 소수는 2진수에서 무한 소수이기 때문에 컴퓨터에 정확히 입력되지가 않습니다.
30.8이 30.7999999로 변환되는 것은 오히려 프로그램이 올바르게 동작했다는 거죠.
실수를 비교에 사용하는 것은 매우 위험합니다. 어느 정도의 tolerance 를 허용하여 해결될 수 있는 경우가 아니면 쓰지 않는게 좋습니다.

Necromancer의 이미지

부동소수점 표현 양식상 어쩔 수 없습니다.
부동소수점 연산은 근사치로 대충 때려맞춰도 되는 곳에만 서야 합니다.

소수점 이하 몇자리까지 정확한 연산이 필요하다면 원값에 x10, x100한 정수 쓰고, 표시할때 자릿수별로 나눠주는게 좋습니다.

Written By the Black Knight of Destruction

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.