Software Development

부동소수점 수의 비교

얼마전에 재민군이 질문했던 내용인데, 태준형의 도움으로 해결했던 문제입니다.

Question

먼저 다음과 같은 코드가 있습니다.

#include 
int main()
{
int i = 10;
double val = i*12.34;
if(val == i * 12.34){
printf("Equaln");
} else {
printf("Not Equaln");
}
}

결과는 무엇일까요?

물론 답은 이 프로그램을 컴파일하고 실행하는 환경에 따라 다르다입니다. 즉, 어셈블리 코드를 생성하는 컴파일러와 이 코드를 실행하는 아키텍쳐에 따라 다를 것이라고 예상할 수 있습니다. 그런데, 신기한 것은 같은 아키텍쳐(Pentium 3)와 같은 컴파일러(gcc 3.4)를 사용하더라도 OS에 따라(Linux와 FreeBSD) 다른 결과가 나온다는 것입니다. 자세히 얘기하면, Linux에서는 “Not Equal”이 출력되고, FreeBSD에서는 “Equal”이 출력됩니다. 어째서 그럴까요?

Answer

설마, 어셈블리 코드가 다르게 나오는 걸까요? OS가 달라서 그럴지도, 컴파일러 설정이 달라서 그럴지도 모르지 않습니까? 아니면 컴파일러 버그? 그런데, 그렇지 않습니다. 부동소수점 연산과 비교를 수행하는 부분의 코드는 다음과 같이 똑같습니다.

movl    $10, -4(%ebp)
fildl   -4(%ebp)
fldl    .LC0
fmulp   %st, %st(1)
fstpl   -16(%ebp)
fildl   -4(%ebp)
fldl    .LC0
fmulp   %st, %st(1)
fldl    -16(%ebp)
fxch    %st(1)
fucompp
fnstsw  %ax
andb    $69, %ah
cmpb    $64, %ah
je  .L3
jmp .L2

간단히 설명하면, 10과 12.34를 FPU stack에 넣고 곱셈을 수행한 후, (FPU stack에 들어있는) 결과를 다시 메모리에 저장합니다. 두번째 연산을 위해 역시 10과 12.34를 FPU stack에 넣고 곱셈을 수행한 후, 메모리에 저장했던 결과도 다시 FPU stack에 불러들여, 두 값의 비교를 수행하는 코드입니다.

두 값의 차이는 곱셈의 결과가 메모리로 잠시 다녀왔는가의 여부 뿐이죠. OS에 따른 결과의 차이도 바로 여기서 발생합니다.

The difference between Linux and FreeBSD is that in Linux, the FPU is operated by default in “extended precision” mode, where 80-bit internal registers are used for floating point numbers. In FreeBSD, the default is to use “double precision” mode, where 64-bit precision is retained.

Excerpt from Resolution of Differences Between X86 Linux/glibc Floating-Point to Integer Conversion Behavior Relative to Other Platforms

즉, 387 내에서 부동소수점 수는 80bit의 정밀도를 가질 수 있는데, 메모리 상에서는 IEEE 754대로 64bit의 정밀도를 가지므로 일종의 rounding error가 일어난다는 거죠. 그리고 FreeBSD에서는 80bit 정밀도 기능을 OS 수준에서 꺼버리지만 Linux는 그렇지 않기 때문에 차이가 나는 것이구요.

More Fun?

참고로, 제 amd64 머신에서는 “Equal”이 출력되고 다음과 같은 코드를 생성합니다.

movl    $10, -4(%rbp)
cvtsi2sd    -4(%rbp), %xmm1
movlpd  .LC0(%rip), %xmm0
mulsd   %xmm1, %xmm0
movsd   %xmm0, -16(%rbp)
cvtsi2sd    -4(%rbp), %xmm1
movlpd  .LC0(%rip), %xmm0
mulsd   %xmm0, %xmm1
movlpd  -16(%rbp), %xmm0
ucomisd %xmm0, %xmm1
jp  .L5
je  .L3

즉, SSE instruction을 사용하죠. gcc는 x86-64 (aka. amd64) 아키텍쳐에서는 기본적으로 SSE를 사용한다고 합니다.

sse Use scalar floating point instructions present in the SSE
instruction set. This instruction set is supported by Pentium3
and newer chips, in the AMD line by Athlon-4, Athlon-xp and
Athlon-mp chips. The earlier version of SSE instruction set
supports only single precision arithmetics, thus the double and
extended precision arithmetics is still done using 387. Later
version, present only in Pentium4 and the future AMD x86-64
chips supports double precision arithmetics too.
For i387 you need to use -march=cpu-type, -msse or -msse2
switches to enable SSE extensions and make this option effec-
tive. For x86-64 compiler, these extensions are enabled by
default.
The resulting code should be considerably faster in the major-
ity of cases and avoid the numerical instability problems of
387 code
, but may break some existing code that expects tempo-
raries to be 80bit.
This is the default choice for the x86-64 compiler.

Pentium 4이상의 머신에서 다음과 같이 컴파일 하면 역시 “Equal”이 출력되는 것을 확인할 수 있습니다. (Pentium 3에서도 컴파일은 되지만, Illegal Instruction 예외가 발생합니다.)

gcc -msse -msse2 -mfpmath=sse test_double.c

Conclusion

문제의 원인은 x86 FPU가 80bit extension을 사용하고 있고, FreeBSD에서는 이 기능을 꺼버리고 Linux는 그대로 두는 것에 있습니다.

어떻게 보면, x86의 FPU가 사용하는 80bit extension이 표준(IEEE754)을 잘 지키지 않은 것이 문제라거나 Linux가 문제의 소지가 있는 CPU 기능을 켜놓은 것이 문제라고 볼 수도 있습니다. 하지만, 모두들 아시다시피 부동소수점 수의 비교 연산은 보장받기 어려운 것이므로, 이런 간단한 비교 연산의 정확도를 버리고 연산의 정밀도를 택한 trade-off는 정당하다고도 생각됩니다.

결국, 부동소수점 수의 비교 연산은 아무리 간단하더라도 rounding error와 같은 문제를 무시해서는 안된다는 것이죠.

위에서 인용했던 글에서 읽어보라고 하고 있는 David Goldberg의 What Every Computer Scientist Should Know About Floating-Point Arithmetic를 읽어볼 생각입니다.

부동소수점 수의 비교 더 읽기"

첫눈 – 첫 인상

첫눈 베타 서비스시작되었군요.

1noon

첫눈을 사용할 때 가장 먼저 눈에 띄는 점이라면 아무래도 구글 스타일의 최소주의 인터페이스와 구글 서제스트와 같은 기능입니다. 국내에서는 어떤 메이저 검색 서비스도 구글 인터페이스를 사용하지 못했다는 점에서 첫눈의 이런 인터페이스는 흥미롭습니다. 국내 사용자들도 이제 광고로 점철된 포탈 페이지로부터 벗어날 수 있을까요? 서제스트 기능은 네이버에서도 채용하고 있긴하지만, 불여우에서 제대로 동작하지 않습니다. 첫눈의 것은 불여우에서도 잘 동작합니다. 괜히 기분 좋네요.

1noon

검색 결과의 처리 방식은 사실 구글 스타일이라기 보다는 국내의 다른 검색 서비스들과 유사합니다. 중요한 사이트나 인기사이트를 맨 위에 올려주고, 뉴스를 그 다음에, 게시판이나 블로그 등의 웹 컨텐츠를 그 아래에 배치하는 식입니다. 흥미로운 것은 검색 결과를 주제별로 분류하는 기능입니다. 더 많이 써봐야 알겠지만, 언뜻 봐서는 상당히 잘 동작하는 것 같습니다. 루나님에 의하면 비비시모라는 해외의 검색 서비스에서 시도하고 있는 것이라고 하는군요. 검색 결과를 처리하는 이러한 두가지 방식은 구글의 방식과는 어느 정도 차이가 있습니다. 그 차이는 검색 결과의 중요도나 내용을 구분해주려는 시도가 사용자에게 얼마나 의미가 있는가하는 의문에 대한 대답일 것입니다. 구글처럼 순서대로 보여주는 것 이상으로 말이죠.

1noon

1noon

검색 결과는 아직(?) 국내에 한정되어 있는 것 같습니다. 지금은 예고편 #1에 불과하니 벌써 실망할 필요는 없으리라고 생각합니다.

광고는 아직(?) 전혀 없습니다. 첫눈의 수익 모델은 아직은 노출이 안된 것 같군요. 아무래도 검색 위주의 서비스에서 가장 궁금하고 기대되는 것은 수익모델입니다.

첫눈 서비스보다 더욱 흥미로운 것은 첫눈의 철학입니다.

"좋은 인재들이 즐겁게 일하기"라는 얘기는 누구나 어떤 회사에서도 할 수 있고 또 하는 얘기입니다만, 정말로 실천되고 있느냐는 별개의 문제입니다. 구글은 그것을 정말로 실천함으로써 유명해진 케이스죠. "검색에 집중"은 정말 어려운 문제라고 생각합니다. 지금까지 국내에 순수한 검색 서비스는 없었다는 점이 그것을 말해줍니다. 역시, 구글은 이것을 해냈기 때문에 유명해진 것입니다. 첫눈은 과연 어떨까요?

첫눈은 사용자에게 주는 새로운 가치를 의미한다고 합니다. 첫눈이 정말로 새로운 가치를 창출하는가는 역시 사용자에게 달려있고, 앞으로 지켜볼 수 밖에 없을 것 같습니다. 첫눈 예고편 #1이 구글처럼 위대한 서비스의 첫 발자욱으로 기억되는 날을 기대해봅니다.

첫눈 – 첫 인상 더 읽기"

g++ 3.4 bug: protected member of template base class hidden from template child class

g++ 3.4도 아직 그다지 안정화 되어 있는 상태는 아닌 모양이다. 이 버그의 workaround 중 하난 멤버에 접근할 때 this->를 사용하는 것이다. 자세한 내용은 다음 링크를 참조.

http://lists.debian.org/debian-gcc/2004/05/msg00293.html

g++ 3.4 bug: protected member of template base class hidden from template child class 더 읽기"

g++ 2.95 bug: internal compiler error when accessing template member function from member function

Description

어떤 멤버 함수로부터 같은 클래스의 템플릿 멤버 함수에 접근할 경우, gcc 2.95가 internal compiler error를 발생시킴.

Example

GCC 2.95 template bug

Workaround

템플릿 멤버 함수 (bar)를 멤버 함수(foo)보다 위쪽에 정의해주면 됩니다. 이 외에도 gcc 2.95는 template 관련 버그가 꽤 많죠. template을 많이 사용한다면 부득이 하지 않은 이상 3.x 이상을 사용할 것을 권장합니다.

g++ 2.95 bug: internal compiler error when accessing template member function from member function 더 읽기"

Database & Search Architecture by Adam Bosworth

Google의 VP of Engineering인 Adam Bosworth와의 토론.

컴퓨팅의 거장, Adam Bosworth는 ITConversations에서 이제 웹의 도래로 인해서 DB는 SQL을 통해서 정해진 문법으로만 access가 가능한 단순한 backend relational DB에서 웹을 통해서 원하는 데이터를 언제든지 더 큰 문맥에서 access할 수 있는 구조를 제안한 다. 구글과 같은 검색엔진이 보여주는 것은 이러한 구조의 일부분이라고 하면서. 좀더 역할을 세분화 시키면서 Bosworth는 사실 데이터를 처리/조작/통합하는 역할을 클라이언트에서 모두 하기에는 시간이 너무 오래 걸릴 수 있는 가능성이 높기 때문에 (예를 들어 프루나나 당나귀같은 p2p 네트워크에서 어떠한 파일 하나를 찾기 위해서 얼마나 오랜 시간이 걸리는지 생각해보면 된다), 이러한 역할을 전담하는 data router라는 개념을 제안한다. 정확한 구조는 알 수 없지만 대략 이러한 방향으로 가는 것이 옳지 않을까 한다. (from 태우’s log)

현재 보편적으로 사용되는 관계형 데이터베이스(Relational Database)들은 주어진 값과 일치하거나 특정 함수에 맞는 결과를 보여주는 데 집중하고 있으나, 검색 엔진들을 보면 실제로 우리가 요구하는 것은 특별한 문맥 상에서 무언가와 관련성을 가지는 결과다. Adam Bosworth는 이를 현재 관계형 데이터베이스들의 한계라고 지적한다. 그가 쓴 데이터베이스에 관한 글도 읽어봄 직하다.

그리고, 데이터의 양이 많아지면서 scalability라는 어려운 문제에 부딪힐텐데, 이를 위한 해결책으로, divide-and-conquer architecture를 제안하고 있다. data router가 query를 특정 back-end 서버군으로 중재해주는 방식을 설명하고 있다. 며칠 전에 철호군과 검색 엔진에서 사용되는 크롤러(crawler)의 network bandwidth bottleneck에 대해서 얘기를 나눈 적이 있다. 분명히 웹의 규모는 앞으로 order of magnitude로 커질 것이고, network bandwidth 이외의 resource에 대해서도 scalability 문제가 발생할 것이다. 이 문제에 대해서 나는 왜 검색엔진이 조금 더 작은 여러 검색엔진의 분산된 구조로 구성되지 않는가에 대해서 의문을 제기했다. 하지만, 철호군은 검색엔진에서의 중복 페이지 제거 등의 이유를 들어주었고, 아마도 그가 옳으리라고 생각한다. 그럼에도 불구하고, 검색 엔진 그리고, 모든 정보 처리 방식에 있어서의 scalability 문제는 좀 더 고민해 볼 필요가 있으리라고 생각된다.

그는 personalization에 대해서도 언급하고 있는데, 그건 이 글을 읽어보도록 하자.

덧붙여, 내 영어 실력에 관한 딴 얘기를 좀 해보자. 이 토론은 무려 1시간짜리에다가, 잘 들리지 않아서, 엄청난 집중력을 요구했다. 결국 30분 가량 듣다가 말았는데, 또렷한 영어 발음을 벗어나면 바로 헤메는 나를 보니, 좀 더 열심히 해야겠다는 생각이 들었다. 전길남 교수님이 수업시간에 했던 얘기가 생각났다. 그런 종류(?)의 토론에서 사람들은 자신의 의견을 빠르게 피력하기 위해 엄청나게 빠르게 말하는 것이 보통이고, non-native-speaker의 입장에서는 "Sorry"라고 말할 겨를은 없다는 것이다. 결국 거기에 걸맞는 능력을 갖출 수밖에 없다는 것이다.

Database & Search Architecture by Adam Bosworth 더 읽기"

Thomas Malone's Perspective in Supernova 2004

Supernova는 기술, 비즈니스와 사회관계는 물론 우리의 삶 전반에 걸쳐 나타나고 있는 decentralization이라는 현상에 관한 컨퍼런스다. 태우님의 글를 통해서 Thomas Malone 교수가 Supernova 2004에서 Perspective로 발표한 내용을 접할 수 있었다. 이 발표가 새롭고 인상적인 생각들을 보여주는 것은 아니라고 하더라도, decentralization 경향에 대한 일반적인 생각을 체계적으로 잘 정리하고 있다고 보여진다. 그렇지 않아도 Complex system연구와 최근의 Social software들을 접하고 나서, 최근 기술과 비즈니스의 decentralization 경향에 대해서 고민을 하고 있었기 때문에 더욱 흥미로운 내용이었다.

그는 정보기술(information technology)이 커뮤니케이션의 비용을 줄임으로써 많은 수의 사람들이 그들 자신의 결정을 위한 충분한 정보를 얻을 수 있게 되었고, 이로 인해 비즈니스에서의 인간의 자유(human freedom)가 증가했을 뿐만 아니라 중요해졌다고 얘기하고 있다. 이러한 현상의 예로 Wikipedia와 eBay를 들고 있다.

또한, 인간의 역사를 돌아보면 문자나 인쇄술, 전화, 인터넷을 통해서 커뮤니케이션의 비용은 계속해서 줄어드는 경향을 보이고 있고, 그러한 경향은 민주주의와 현재 시작되고 있는 decentralized decison-making의 현상을 낳았다. 커뮤니케이션의 비용을 줄여주는 기술이 발전한다는 것이 사람들이 decentralized decison-making을 원한다는 것을 의미하지는 않는다. 하지만, 현재와 같이 지식과 혁신에 기반한 경제(knowledge-based and innovation-driven economy)에서 그것은 동기(motivation), 창조성(creativity), 혁신(innovation), 즐거움(enjoyment) 등의 이익을 사람들에게 제공해주기 때문에 앞으로 점점 더 중요한 현상이 될 것이라고 그는 얘기한다.

그는 이러한 decentralization의 방향을 세가지로 제시하고 있다. 바로 loose hierarchy, democracy, market이다. Thomas는 이들에 대해 예를 들어 잘 설명하고 있으므로 여기서는 설명하지 않겠다. 직접 내용을 참고하기 바란다.

그는 앞으로의 비즈니스에서는 각각의 개인들이 우주의 중심에서서 자신들의 결정을 내리기 위한 모든 종류의 정보에 접근할 수 있을 것이라고 얘기한다. 또한 위에서 언급한 세가지의 방향들을 위한 새로운 어플리케이션들(하드웨어, 네트워크, 소프트웨어)이 탄생할 것이라고 예측하고 있다. 그리고 무엇보다도 그러한 어플리케이션에 적합한 비즈니스 조직에 대한 고민이 필요해질 것이라고 얘기하고 있다.

Thomas Malone's Perspective in Supernova 2004 더 읽기"

On the Criteria To Be Used in Decomposing Systems into Modules

"On the Criteria To Be Used in Decomposing Systems into Modules"Information Hiding에 관해 얘기할 때 항상 인용되는 David Parnas의 paper다. 전에 읽을 때는 그저 Information Hiding의 개념을 처음으로 도입한 논문이구나 하고 생각했는데, Original wiki의 Information Hiding 엔트리를 살펴보다가 링크가 걸린 "Abstraction, Encapsulation, and Information Hiding"이란 글에서 다음과 같은 내용을 발견하였다.

"Hiding information", in and of itself, was not new. For that matter, the isolation of difficult and/or likely-to-change design decisions in modules was also not new. (Dijkstra had done this earlier in his implementation of the "THE"-Multiprogramming System.) The significance of Parnas’s 1972 article on software module specification lay in two areas:

– His advocation and specification of the (then innovative) technique of basing system modularization on design decisions. (You would have to say that the article presented a significantly different view of Dijkstra’s "levels of abstraction" approach.)

– His use of the term "information hiding". Virtually every article which mentions the topic traces its origin to [Parnas, 1972b].

Obviously, Parnas did not say all information hiding is good, nor did he say that all information hiding techniques are equally useful. He was identifying a particularly pragmatic approach to information hiding.

On the Criteria To Be Used in Decomposing Systems into Modules 더 읽기"

Language Workbenches

Domain Specific Language (DSL) 개발의 부담을 덜어주는 Language Workbenches 개념에 대해 소개하는 Martin Fowler의 글. DSL에 관심이 있다면 읽어보길 바란다. DSL 자체에 대해서도 예를 들어 잘 설명하고 있으므로, DSL에 대해서 잘 모르더라도 읽는데에는 별 문제가 없다. Martin Fowler의 쉽게 풀어쓰는 글솜씨에도 놀랐지만, 링크되어 있는 수많은 Further Reading들을 보면 새삼 공부할 거리가 많다는 것을 느낀다.

http://martinfowler.com/articles/languageWorkbench.html

http://martinfowler.com/bliki/LanguageWorkbenchReadings.html

키워드들.

Domain Specific Langauge, Language Oriented Programming, Language Workbench, concrete syntax, abstract syntax, External DSL, Internal DSL, Intentional Software, Meta-Programming System, Software Factories, Model Driven Architecture, schema, editor, generator, abstract representation, projectional editor, COBOL inference, graphical DSL, lay programmer, domain expert.

Language Workbenches 더 읽기"

Summer of Code Proposal

Summer of Code에 지원했다. Project description은 아래와 같다. 크게 formal할 필요는 없을 것 같아서 대충 썼는데… 벌써 5700명이나 지원했다고 하니, 이 정도로는 뽑히기는 좀 힘들지 않을까 싶기도 하고… 구글의 application form의 Country 항목에는 South Korea가 위쪽에 올라와있는데, 의외로 우리나라의 지원자는 10명 뿐이다. PST로 14일이 마감이니 지원할 생각이 있으나 못하신 분들은 얼른 하시길.

Ruby .NET – Ruby compiler on .NET platform.

INTRODUCTION

Ruby .NET is an Ruby compiler implementation on Microsoft .NET platform. Not just adding another .NET language, but Ruby developers could interop with .NET platform easily. Besides, we can expect some performance boost.

Project Page (temporary): http://lastmind.net/twiki/bin/view/Main/RubyDotNet

APPROACH

Now I’m considering IronPython approach: Iron Python (http://www.ironpython.com/) is a C# implementation of Python interpreter and works on CLR. It shows better performance than original Python interpreter implementation. I’ve investigate IronPython code and found that it just parse Python code, build AST, and translate to CLI code using .NET reflection. I believe I can take same appraoch in Ruby .NET.

ISSUES

– There’s no authentic Ruby syntax reference except the original Ruby compiler implementation.

"I’m afraid, the only complete reference for Ruby’s grammar is the YACC input file "parse.y" from the ruby source distribution."

(from http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/15608)

– Semantics for Ruby statements is just hardwired into Ruby compiler implementation.

RELATED PRODUCTS

Of course, I’ve seen around several efforts to develop Ruby .NET, but I believe no production-quality project is available so that It’s worth to trying.

IronPython: http://www.ironpython.com/
JRuby: http://jruby.sourceforge.net/
rubydotnet: http://rubydotnet.sourceforge.net/
NetRuby: http://www.geocities.co.jp/SiliconValley-PaloAlto/9251/ruby/nrb.html
Ruby .NET Compiler: http://www.asakawa.net/ruby/rubynet_memo.html
Ruby – Perl Parse::RecDescent grammar: http://cpan.uwinnipeg.ca/htdocs/parrot/Ruby.html
RubySharp: http://www.pronovomundo.com/htu/theses2004/rubysharp_hedstrom_thesis_final_040526.pdf

EXPERIENCE

Compiler theory is a crucial skill to develop Ruby .NET. I’ve finished compiler course for undergraduate in KAIST and I believe I have. I have 3.5 year C/C++ system/network programming experience in POSIX platform as an alternative job to military service. I am specially interested in distributed computing technologies in enterprise environment like several RPC technologies, CORBA, DCOM, .NET Remoting, Indigo, and Web Services. I love to learn programming languages like C/C++, Java, C#, Perl, Python, Ruby. Ruby is my favorite one these days.

Update: tj형의 의견에 따라 좀 더 formal하게 대폭 수정해서 다시 application 제출 했다.

Summer of Code Proposal 더 읽기"