부동소수점 합계 오차, NaN, -0.0 실측

숫자 강좌 6. 1000만 개 합계는 396 ulp 어긋났고 Python 3.12 sum() 은 오차 0. -ffast-math 는 Kahan 보정을 지우고 0/0 을 1 로 만들었다. NaN 정렬 붕괴와 시리즈 총정리.

시리즈: 숫자·금액 계산 강좌 6/6 (고급). 부동소수점 수를 많이 더하는 사람을 위한 마지막 강좌입니다. 로그 집계, 센서 데이터, 점수, 통계, 게임 물리, 지난달 숫자와 맞아야 하는 월간 리포트. 다 읽으면 합계 오차가 얼마나 빨리 커지는지, 어떤 알고리즘이 그것을 없애고 비용은 얼마인지, NaN 과 음의 0 이 정렬·max·JSON 에 무슨 짓을 하는지, -ffast-math 가 C 코드에서 무엇을 조용히 지우는지 알 수 있습니다. 끝에 시리즈 전체 체크리스트를 붙였습니다.

1강에서 0.1 + 0.2 가 0.30000000000000004 가 되는 것을 봤습니다. 17번째 자리 하나쯤은 무해해 보입니다. 이번에는 그것을 1000만 번 반복하면 어떻게 되는지, 그리고 비교 방식이 특이해서 코드 리뷰를 빠져나가는 두 특수값 NaN 과 -0.0 을 다룹니다. 국내 서비스는 원을 정수로 들고 있어서 금액 자체는 안전한 경우가 많지만, 포인트 적립률, 할인 안분, 환율 환산, 평균·분산처럼 중간에 소수가 되는 값을 집계할 때 같은 문제가 나옵니다. Apple Silicon Mac (M1 Pro) 에서 Python 3.9.6 과 3.12.14, Apple clang 21, macOS 의 JavaScriptCore 셸 jsc, PHP 8.5.6, Java 26, .NET 10.0.14, Swift 6.4.2, SQLite 3.51.0 을 돌렸습니다. 난수는 random.Random(42) 라서 모든 숫자를 재현할 수 있습니다.

0.1 을 1000만 번 더하기

가장 단순한 실험입니다. 0.1 을 10,000,000 번 더합니다. 정답은 1,000,000 입니다.

처리계 결과
Python 3.9.6 루프와 sum() 999999.9998389754
Python 3.12.14 sum() 1000000.0
Python math.fsum (두 버전) 1000000.0
jsc for 루프 999999.9998389754
PHP 8.5.6 루프 (var_export) 999999.9998389754
C# double 루프 999999.9998389754
C# decimal (0.1m) 루프 1000000.0
SQLite 3.51.0 에서 10^7 행 sum(0.1) 1000000.0

오차는 0.00016 으로, 리포트 합계를 검산하는 사람 눈에 보이는 크기입니다. 조용한 변화에 주의하세요. Python 3.12 의 내장 sum() 은 보정 합산(Neumaier 방식)으로 바뀌어서, 같은 스크립트라도 Python 만 올리면 합계가 달라집니다. SQLite sum() 도 여기서는 정확했습니다. 최근 SQLite 는 같은 계열의 보정 합산(Kahan-Babuska-Neumaier)을 씁니다. PHP echo 는 999999.99983898 로 세 자리를 숨겼고 var_export 에서 전체 값이 보였습니다.

또 하나의 단골 예제는 math.fsum([1e100, 1.0, -1e100]) 입니다. fsum 은 1.0, 단순 루프와 Python 3.9 sum() 은 0.0 이었습니다. 1e100 에 더하는 순간 1.0 이 사라지기 때문입니다. Python 3.12 sum() 은 1.0 을 돌려줬습니다.

개수 N 에 따라 오차가 커지는 모습

+ 할 때마다 결과가 가장 가까운 double 로 반올림되므로, 단순 루프는 원소 하나당 한 번의 반올림 오차를 만듭니다. 오차끼리 일부 상쇄돼서 N 에 비례할 만큼은 아니지만, 커지기는 합니다. 오차는 ulp(unit in the last place: 그 크기에서 이웃한 double 사이 간격) 단위로 쟀고, 올바르게 반올림된 합을 돌려주는 math.fsum 을 기준으로 했습니다. 확인 삼아 앞 10^6 개를 Fraction 으로 정확히 더한 값이 fsum 과 비트 단위로 같다는 것도 확인했습니다.

def naive(xs):
    s = 0.0
    for x in xs: s += x
    return s

def kahan(xs):
    s = c = 0.0
    for x in xs:
        y = x - c; t = s + y; c = (t - s) - y; s = t
    return s

def neumaier(xs):
    s = c = 0.0
    for x in xs:
        t = s + x
        if abs(s) >= abs(x): c += (s - t) + x
        else: c += (x - t) + s
        s = t
    return s + c

[0, 1) 균등 난수 (fsum 과의 차이, ulp):

N 단순 루프 오름차순 정렬 후 쌍별 합산 Kahan Neumaier
10^3 1 2 0 0 0
10^4 0 5 0 0 0
10^5 54 50 0 0 0
10^6 33 102 0 0 0
10^7 396 64 1 0 0

부호와 크기가 섞인 값 (uniform(-1, 1) * 10**randint(-8, 8)):

N 단순 루프 오름차순 정렬 후 절댓값 정렬 쌍별 합산 Kahan Neumaier
10^3 15 372 4 8 1 0
10^5 64 5,155 3 2 0 0
10^6 129 3,912 55 0 0 0
10^7 532 54,006 44 1 0 0
1000만 개를 더했을 때 오차(ulp) 막대그래프. 단순 루프 396, 오름차순 정렬 후 루프 64, 쌍별 합산 1, Kahan 0, Neumaier 와 Python 3.12 sum 0, math.fsum 0.
[0,1) 균등 난수 10^7 개(시드 42)를 방법별로 더했을 때의 오차. 기준은 올바르게 반올림한 합(math.fsum, 10^6 개에서 Fraction 정확 계산과 일치 확인). 여기서 1 ulp 는 약 9.3e-10. 단순 루프는 Python 3.9 sum() 과 같은 값이었고 Python 3.12 sum() 은 오차 0 이었다.

교훈은 둘입니다. 첫째, 오차는 N 에 대해 단조 증가하지 않습니다(10^5 에서 54, 10^6 에서 33). 무작위 걸음이라서 어떤 데이터에서 통과한 테스트가 다른 데이터에서 실패합니다. 둘째, "작은 것부터 정렬해서 더하라"는 통념은 양수에서는 통했지만 부호가 섞이자 100배 나빠졌습니다. 큰 음수가 먼저 더해져 합계가 커지고, 그 큰 수가 작은 값들을 삼키기 때문입니다. 절댓값으로 정렬하면 나아졌지만 그래도 44 ulp 가 남았습니다. 보정 합산은 둘 다 이깁니다.

정확함의 비용

10^6 개, 5회 중앙값, Apple Silicon Mac (M1 Pro):

방법 Python 3.9.6 Python 3.12.14
내장 sum() 2.5 ms (보정 없음) 2.5 ms (보정 있음)
단순 for 루프 19.2 ms 11.8 ms
math.fsum 16.6 ms 7.3 ms
Kahan for 루프 59.8 ms 32.5 ms
sum(sorted(xs)) 169.0 ms 153.4 ms
Decimal 값의 sum() 39.2 ms 41.7 ms

Python 에서는 답이 간단합니다. 3.12 라면 sum() 이 가장 빠르면서 보정까지 하고, 그 전 버전이라면 math.fsum 이 정확하면서도 직접 짠 루프보다 빠릅니다. 정렬은 가장 느리고 가장 정확하지도 않습니다.

C 에서 double 10^7 개(clang -O2, 5회 중 최고)는 단순 루프 9.71 ms, Kahan 37.37 ms 였습니다. Kahan 은 매 단계가 앞 단계에 의존해서 CPU 가 덧셈을 겹쳐 실행하지 못하므로 약 4배 걸립니다. 정산 집계라면 싼 값이고, 게임 프레임 처리라면 비싼 값입니다.

float32 와 float64

float(32비트)는 십진 유효숫자 약 7자리, double(64비트)은 약 16자리입니다. 같은 10^7 개를 C 로 더했습니다.

double naive             sum=4998269.0432556057  abs err=-3.69e-07  9.71 ms
double kahan             sum=4998269.0432559745  abs err=0          37.37 ms
float naive              sum=4998096.5           abs err=-173       9.32 ms
float data, double acc   sum=4998269.0432599643  abs err=3.99e-06   9.33 ms
float: add 1.0f twenty million times -> 16777216.0

float 변수에 누적하자 500만 중 173 이 어긋났습니다. 4,194,304(2^22)를 넘으면 이웃한 float 사이 간격이 0.5 가 되어, [0, 1) 의 값이 0, 0.5, 1 중 하나로 반올림된 뒤 더해지기 때문입니다. 데이터는 float 로 두고 합계만 double 로 내면 시간은 그대로이고 오차는 저장값 자체의 반올림 몫인 4e-6 까지 내려갔습니다. 마지막 줄이 가장 극적입니다. float 카운터는 2^24 = 16,777,216 에서 멈춥니다. 16,777,216 + 1 이 반올림되어 다시 16,777,216 이 되기 때문입니다. Java 의 같은 루프는 1.6777216E7, jsc Math.fround(16777217) 은 16777216 이었습니다.

원칙: float32 는 큰 배열 저장(머신러닝 가중치, 정점 데이터)과 GPU 연산에는 충분합니다. 합계·평균·분산은 float64 로 냅니다.

-ffast-math: 빨라지고, 결과가 바뀌고, NaN 이 안 보인다

-ffast-math 는 clang 에게 "부동소수점을 실수 대수처럼 다뤄도 된다"고 알립니다. 덧셈 순서를 바꾸고, NaN 과 무한대는 없다고 가정하고, 0 의 부호를 무시합니다. 같은 파일을 두 가지로 컴파일했습니다.

출력 -O2 -O2 -ffast-math
double 단순 합계 (10^7) 오차 -3.69e-07, 9.71 ms 오차 3.45e-08, 1.75 ms
double Kahan 합계 오차 0, 37.37 ms 오차 3.45e-08, 1.58 ms
float 단순 합계 오차 -173 오차 18
1.0f 를 2×10^7 번 더하기 16777216.0 20000000.0
z = atof("0") 일 때 z/z nan 1.000000
v = strtod("nan") 일 때 v != v 1 0

무슨 일이 일어났을까요. 순서 바꾸기가 허용되자 clang 은 단순 루프를 여러 부분합으로 벡터화했습니다. 5.5배 빨라졌고, 우연히 정확도도 올라갔습니다(얕은 쌍별 합산과 같은 꼴이 되므로). 반면 Kahan 의 보정 (t - s) - y 는 대수적으로 0 이라고 증명하고 지워 버렸습니다. Kahan 함수가 단순 함수와 완전히 같은 값을 냈습니다. float 카운터 루프는 20,000,000 이라는 계산된 값으로 바뀌었습니다. 엄격한 IEEE 연산으로는 나올 수 없는 "정답"입니다. 그리고 NaN 이 없다고 가정하므로 실행 중 0/0 이 1.0 이 되었고, 자기 자신과 비교하는 NaN 검사는 거짓이 됐습니다. noinline 도우미 함수 안의 라이브러리 호출 isnan() 만 여전히 동작했고, NAN 매크로를 쓴 줄에는 clang 이 warning: use of NaN is undefined behavior due to the currently enabled floating-point options [-Wnan-infinity-disabled] 를 냈습니다.

보정 합산, 구간 연산, NaN 검사가 있는 코드는 -ffast-math 로 컴파일하지 마세요. 속도가 필요하면 누적 변수를 여러 개 두거나 쌍별 합산을 직접 쓰면, 컴파일러에게 거짓말하지 않고도 벡터화됩니다. 최적화가 가정을 이용하는 이야기는 C 의 정의되지 않은 동작과 최적화를 참고하세요.

NaN: 비교, 정렬, max, JSON

NaN(Not a Number)은 0/0, inf - inf, 0 * inf, 파싱 실패, 결측 데이터에서 생깁니다. 결정적인 성질은 NaN 과의 비교는 모두 거짓, NaN == NaN 도 거짓이라는 점입니다. 정렬과 max 는 일관된 순서를 전제로 하므로, NaN 위치에 따라 망가지는 모양이 달라집니다.

Python  sorted([3, nan, 1, 2])          -> [3, nan, 1, 2]        (정렬 안 됨)
Python  sorted([nan, 3, 1, 2])          -> [nan, 1, 2, 3]
Python  max([nan, 1, 2]) / max([1, 2, nan]) -> nan / 2
Python  statistics.median([1, nan, 3, 2, 5]) -> 2
Swift   [3, .nan, 1, 2].sorted()        -> [3.0, nan, 1.0, 2.0]
jsc     [3, NaN, 1, 10, 2].sort((a, b) => a - b) -> 3,NaN,1,2,10
jsc     [3, NaN, 1, 10, 2].sort()       -> 1,10,2,3,NaN          (문자열 순)
PHP     sort([3, NAN, 1, 2])            -> [NAN, 1, 2, 3]
PHP     max(NAN, 1) / max(1, NAN)       -> 1 / NAN
Java    Arrays.sort(double[])           -> [-0.0, 0.0, 1.0, 2.0, 3.0, NaN]
C#      Array.Sort(double[])            -> NaN, 0, -0, 1, 2, 3
C#      Math.Max(NaN, 1) / LINQ Max([1, NaN, 2]) -> NaN / 2

Java 와 C# 은 정렬용 전순서를 정의하고 있어서(Java 는 NaN 을 맨 뒤, C# 은 맨 앞) 제대로 정렬됩니다. 나머지는 오류 없이 엉뚱한 결과를 냅니다. median 은 2 라는 그럴듯한 값까지 돌려줬습니다. 무서운 것은 눈에 보이는 NaN 이 아니라 그 옆의 틀린 답입니다.

포함 여부도 이상합니다. Python 에서 nan in [nan] 은 True(같은 객체라 동일성으로 찾음)지만 float('nan') in [float('nan')] 은 False 이고, {float('nan'), float('nan')} 은 원소가 2개입니다. jsc 에서는 [NaN].includes(NaN) 이 true 인데 [NaN].indexOf(NaN) 은 -1, isNaN("abc") 는 true 이고 Number.isNaN("abc") 는 false 였습니다.

JSON 에는 NaN 이 없습니다. 언어별 반응입니다.

호출 결과
Python json.dumps([nan, inf, -0.0]) [NaN, Infinity, -0.0] (올바른 JSON 이 아님)
Python json.dumps(nan, allow_nan=False) ValueError: Out of range float values are not JSON compliant
jsc JSON.stringify([NaN, Infinity, -0]) [null,null,0]
jsc JSON.parse('[NaN]') SyntaxError: JSON Parse error: Unexpected identifier "NaN"
PHP json_encode(NAN) false, Inf and NaN cannot be JSON encoded
.NET JsonSerializer.Serialize(double.NaN) ArgumentException: .NET number values such as positive and negative infinity cannot be written as valid JSON.
SQLite INSERT … VALUES (9e999 - 9e999) NULL 로 저장

Python 서비스가 NaN 을 내보내면 JavaScript 쪽 JSON.parse 가 실패하고, JavaScript 쪽은 NaN 을 조용히 null 로 바꾸고, SQLite 는 NULL 로 바꿔서 sum() 과 avg() 가 건너뜁니다. 경계에서 정하세요. 입력에서 NaN 을 거부하고, 값이 없을 때는 의도적으로 null 을 보냅니다. 큰 정수 ID 와 JSON 문제는 2강에서, PHP 사례는 prepared statement 가 JSON 숫자 타입을 바꾸는 이야기에 있습니다.

음의 0

IEEE 754 에는 0 이 두 개 있습니다. -0.0 == 0.0 은 시험한 모든 언어에서 참이지만, 부호는 새어 나옵니다.

  • 1 / -0.0 은 C 에서 -inf, jsc 에서 -Infinity.
  • Python repr(-0.0) 은 -0.0. max(0.0, -0.0) 은 0.0, max(-0.0, 0.0) 은 -0.0(동점이면 앞 인자가 이김).
  • 작은 음수를 포맷하면 마이너스가 붙습니다. Python f"{-0.004:.2f}", jsc (-0.004).toFixed(2), Java String.format("%.2f", -0.004), .NET ToString("F2") 모두 -0.00. jsc Intl.NumberFormat("en-US", {maximumFractionDigits: 2}) 는 -0, PHP number_format(-0.004, 2) 는 0.00 이었습니다.
  • Java Double.valueOf(0.0).equals(-0.0) 은 false 라서 HashMap<Double, …> 은 둘을 다른 키로 가집니다.
  • JSON.stringify(-0) 은 0, PHP json_encode(-0.0) 은 -0, SQLite 는 -0.0 을 0.0 으로 저장했습니다.

환율 환산 차액 0.004 달러가 정산 화면에 -0.00 으로 찍히는 것이 현장에서 가장 흔한 민원입니다. 먼저 통화의 최소 단위로 반올림하고(4강), 표시 전에 0 인지 검사해서 정규화하세요.

흔한 실수 → 실제 결과 → 고치는 법

실수 실제 결과 고치는 법
수백만 개 float 을 루프로 합산 10^7 개에서 396 ulp 어긋남 math.fsum, Neumaier, Python 3.12+ sum()
"정렬하고 더하기" 부호가 섞이면 54,006 ulp 보정 합산 사용
float32 누적 변수 500만 중 173 어긋남 float64 로 누적
float 카운터 16,777,216 에서 멈춤 정수 카운터
Kahan + -ffast-math 보정이 지워짐 그 파일은 fast-math 없이
NaN 섞인 sorted() / max() 정렬 안 됨, 순서 따라 바뀌는 max 먼저 isnan 으로 제거
NaN 을 json.dumps 다른 언어가 못 읽는 JSON allow_nan=False, null 전송
작은 음수를 반올림해 표시 -0.00 반올림 후 0 정규화
합계를 == 로 비교 Python 3.9 와 3.12 합계가 다름 정수 원 단위, 또는 math.isclose

int, Decimal, float 고르기

판단 그림. 먼저 숫자가 무엇을 나타내는지 본다. 돈·세금처럼 영수증과 맞아야 하는 값은 최소 단위 정수, 비율이나 1원 미만 단수가 필요하면 Decimal. 개수와 ID 는 64비트 정수, 2의 53제곱을 넘을 수 있으면 JSON 에서는 문자열. 측정값·통계·그래픽스는 float64. float32 는 저장이나 GPU 용으로만 쓰고 합계는 float64 로 낸다. 부동소수점 수를 많이 더할 때는 fsum, Neumaier, 쌍별 합산을 쓰고 NaN 은 입력 경계에서 거부한다.
어떤 숫자 타입을 쓸지 정하는 그림. 각 갈래는 이 시리즈의 실행 결과에 근거한다. 0.1 을 1000만 번 더하면 double 은 999999.9998389754, C# decimal 은 정확히 1000000.0. float32 는 16777216 에서 더 세지 못했고, NaN 은 Python·Swift·jsc 의 정렬을 망가뜨렸다.
  • 돈, 세금, 영수증과 맞아야 하는 값: 최소 단위 정수(원이라면 그대로 정수), 비율이나 1원 미만 단수가 필요하면 Decimal(4강). C# decimal 은 0.1 을 1000만 번 더해 정확히 1000000.0 이었습니다.
  • 개수와 ID: 64비트 정수. 2^53 을 넘을 수 있으면 JSON 에서는 문자열(2강).
  • 측정값, 통계, 머신러닝, 그래픽스: float64. float32 는 저장이나 GPU 용으로만, 누적은 float64. N 이 크거나 실행 간 결과를 비교한다면 fsum 이나 보정 합산으로 더합니다.

시리즈 총정리 체크리스트

  1. 타입을 안다: 0.1 같은 리터럴은 처음부터 2진수로 반올림돼 있다(1강). float 은 허용 오차로 비교하고 == 를 쓰지 않는다.
  2. 한계를 안다: 2^31, 2^63, 그리고 JavaScript 숫자와 JSON 의 2^53(2강).
  3. 반올림 방식을 의도적으로 고르고(짝수 반올림인지 사사오입인지), 마지막에 한 번만 반올림한다. 10원 단위 절사 같은 규칙과 부가세 계산은 관련 기관 안내나 세무사에게 확인한다(3강).
  4. 돈은 최소 단위 정수나 Decimal 로 두고 float 로 두지 않는다(4강).
  5. 포맷한 숫자를 저장하지 않는다. 입력은 로캘을 명시하고 엄격한 패턴으로 읽는다(5강).
  6. 보정 합산으로 더하고, NaN 은 경계에서 막고, -0.0 은 표시 전에 정규화하고, 숫자의 정확성이 중요한 코드에 -ffast-math 를 쓰지 않는다(이번 강좌).
  7. 경계값 테스트를 쓴다: 0.1, 2.5, -0.0, NaN, 2^53 + 1, 빈 입력. 첫 단위 테스트도 참고.

솔직한 한계와 버전 메모

  • Python sum() 이 float 에 보정 합산(Neumaier)을 쓰는 것은 3.12 부터입니다. 3.9.6(보정 없음)과 3.12.14(보정 있음)에서 확인했습니다.
  • SQLite sum(0.1) 이 정확했던 것은 3.51.0 에서의 측정입니다. 오래된 SQLite 는 단순 합계였으니 쓰는 버전을 확인하세요.
  • -ffast-math 결과는 arm64 의 Apple clang 21 기준입니다. GCC 나 다른 타깃은 재배열 방식이 다릅니다. Kahan 보정 삭제와 NaN 접기는 흔한 경향이지만 숫자 자체는 이식되지 않습니다. 리눅스에서는 돌리지 않았습니다.
  • ulp 단위 오차는 데이터에 따라 다릅니다. 두 데이터는 random.Random(42) 로 만들었습니다. 다른 분포에서는 숫자가 바뀌지만 방법들의 순위는 같았습니다.
  • 시간은 한 대(M1 Pro)에서 5회 중 최고값 또는 중앙값이며, 그 이상의 워밍업 제어는 하지 않았습니다.

연습 문제

1. 대시보드가 Python 3.9 로 주문 금액(float) 200만 건을 합산하고, Python 3.12 작업이 낸 어제 숫자와 비교합니다. 10번째 자리가 다릅니다. 원인과 해결책 두 가지를 말해 보세요.

2. 실행하지 말고 답하세요. Python 에서 max([float("nan"), 5.0]) 와 max([5.0, float("nan")]) 는 무엇을 출력할까요? 대신 어떻게 쓰겠습니까?

정답

1. Python 3.12 의 sum() 은 보정 합산이고 3.9 는 아니라서 아래 자리가 어긋납니다(0.1 실험에서 999999.9998389754 대 1000000.0). 해결책은 두 작업 모두 math.fsum 을 쓰는 것. 매출이라면 더 좋은 방법은 원을 정수로 저장해 정수를 더하는 것으로, 어느 버전에서나 정확합니다.

2. 차례로 nan 과 5.0 입니다. max 는 첫 원소를 들고 있다가 x > 현재값 이 참일 때만 바꾸는데, NaN 이 어느 쪽에 있든 그 비교는 참이 되지 않습니다. max(x for x in xs if not math.isnan(x)) 로 쓰고, 입력의 NaN 이 무엇을 뜻하는지(대개는 더 앞에서 거부해야 함) 명시적으로 정합니다.

이어서

자주 묻는 질문

작은 수부터 정렬해서 더하면 오차가 줄어드나요?

항상 그렇지는 않습니다. [0,1) 의 양수 1000만 개에서는 오름차순 정렬로 오차가 396 ulp 에서 64 ulp 로 줄었지만, 부호와 크기가 섞인 데이터에서는 532 ulp 에서 54,006 ulp 로 오히려 나빠졌습니다. Kahan, Neumaier, math.fsum, Python 3.12 sum() 은 시험한 모든 경우에 0~1 ulp 였습니다.

NaN 이 섞이면 Python sorted() 는 어떻게 되나요?

NaN 과의 비교는 모두 False 라서 정렬 알고리즘이 모순된 답을 받습니다. sorted([3, nan, 1, 2]) 는 [3, nan, 1, 2] 그대로 돌아왔고, max([nan, 1, 2]) 는 nan, max([1, 2, nan]) 은 2, statistics.median([1, nan, 3, 2, 5]) 는 2 였습니다. Swift 와 jsc 도 같은 식으로 망가집니다. 정렬 전에 math.isnan 으로 걸러 내세요.

-ffast-math 를 켜도 괜찮을까요?

정확한 반올림이나 NaN 에 기대지 않는 코드에서만 그렇습니다. clang -O2 -ffast-math 에서 Kahan 합산은 단순 루프와 같은 결과가 됐고, 실행 중 계산한 0.0/0.0 은 nan 이 아니라 1.000000 으로 찍혔으며, 입력에서 읽은 NaN 에 대해 v != v 가 0 이었습니다. 단순 합계는 5.5배 빨라졌고 우연히 더 정확해졌습니다.

  • #숫자 계산
  • #고급
  • #부동소수점
  • #NaN
  • #Kahan 합산
  • #Python
  • #C
  • #JavaScript
  • #SQLite

t.laboratory

직접 만들고 운영하는 앱·서버·서비스에서 겪은 문제와, 그 해결에 쓴 코드를 기록하는 기술 블로그입니다.

운영자 소개 · 앱

↑