아니요, 이것은 또 다른 “왜 (1 / 3.0) * 3! = 1” 질문 이 아닙니다 .
최근에 부동 소수점에 대해 많이 읽었습니다. 특히, 동일한 계산이 다른 아키텍처 또는 최적화 설정에서 다른 결과 를 제공 할 수있는 방법 .
재생을 저장하거나 P2P 네트워크로 연결된 비디오 게임 (서버-클라이언트와 달리)의 문제로, 모든 클라이언트가 프로그램을 실행할 때마다 정확히 동일한 결과를 생성합니다-하나의 작은 불일치 부동 소수점 계산은 다른 컴퓨터에서 (또는 동일한 컴퓨터에서 ) 게임 상태가 크게 달라질 수 있습니다 !
이것은 일부 프로세서 (즉, x86)가 배 확장 정밀도를 사용하기 때문에 IEEE-754 를 “따르는”프로세서에서도 발생합니다 . 즉, 80 비트 레지스터를 사용하여 모든 계산을 수행 한 다음 64 비트 또는 32 비트로 잘라내어 계산에 64 비트 또는 32 비트를 사용하는 머신과 다른 반올림 결과를 가져옵니다.
온라인 에서이 문제에 대한 몇 가지 솔루션을 보았지만 C #이 아닌 C ++에 대한 모든 솔루션을 보았습니다.
- (Windows), (Linux?) 또는 (BSD)를
double
사용하여 배정 밀도 모드를 비활성화합니다 (모든 계산에서 IEEE-754 64 비트를 사용하도록 )._controlfp_s
_FPU_SETCW
fpsetprec
- 항상 동일한 최적화 설정으로 동일한 컴파일러를 실행하고 모든 사용자에게 동일한 CPU 아키텍처가 있어야합니다 (플랫폼 간 재생 없음). 내 “컴파일러”는 실제로 JIT이므로 프로그램이 실행될 때마다 다르게 최적화 될 수 있기 때문에 이것이 가능하지 않다고 생각합니다.
- 고정 소수점 연산, 그리고 피할 사용
float
및double
모두를.decimal
이 목적으로 작동하지만 훨씬 느려질 것이며System.Math
그것을 지원 하는 라이브러리 함수는 없습니다.
그래서 이것은 C #에서도 문제가됩니까? 모노 만 아닌 Windows 만 지원하려면 어떻게해야합니까?
그렇다면 프로그램을 정상적인 배정도로 실행하도록 강제 할 수있는 방법이 있습니까?
그렇지 않은 경우 부동 소수점 계산을 일관성있게 유지 하는 데 도움이되는 라이브러리가 있습니까?
답변
.net에서 일반 부동 소수점을 결정적으로 만드는 방법은 없습니다. JITter는 다른 플랫폼 (또는 다른 버전의 .net)에서 다르게 동작하는 코드를 작성할 수 있습니다. 따라서 float
결정적 .net 코드에서 normal s를 사용할 수 없습니다.
내가 고려한 해결 방법 :
- C #에서 FixedPoint32를 구현하십시오. 이것이 너무 어렵지는 않지만 (반쯤 완성 된 구현이 있습니다) 매우 작은 범위의 값은 사용하기가 성가 시게합니다. 항상주의를 기울여야하므로 오버 플로우 나 정밀도가 떨어질 수 있습니다. 결국 나는 정수를 직접 사용하는 것보다 쉽지 않다는 것을 알았습니다.
- C #에서 FixedPoint64를 구현하십시오. 나는 이것이 오히려 어렵다는 것을 알았다. 일부 연산의 경우 128 비트의 중간 정수가 유용합니다. 그러나 .net은 그러한 유형을 제공하지 않습니다.
- 사용자 정의 32 비트 부동 소수점을 구현하십시오. BitScanReverse 내장 함수가 없으면이를 구현할 때 몇 가지 문제가 발생합니다. 그러나 현재 이것이 가장 유망한 길이라고 생각합니다.
- 수학 연산에는 기본 코드를 사용하십시오. 모든 수학 연산에서 델리게이트 호출의 오버 헤드가 발생합니다.
방금 32 비트 부동 소수점 수학의 소프트웨어 구현을 시작했습니다. 내 2.66GHz i3에서 초당 약 7 천만 더하기 / 곱하기를 할 수 있습니다.
https://github.com/CodesInChaos/SoftFloat . 분명히 여전히 매우 불완전하고 버그가 있습니다.
답변
C # 사양 (§4.1.6 부동 소수점 유형)에서는 특히 결과보다 높은 정밀도를 사용하여 부동 소수점 계산을 수행 할 수 있습니다. 따라서 아니요, .Net에서 직접 계산을 결정할 수 있다고 생각하지 않습니다. 다른 사람들은 다양한 해결 방법을 제안 했으므로 시도해 볼 수 있습니다.
답변
다음 페이지는 그러한 작업의 절대 이식성이 필요한 경우에 유용 할 수 있습니다. 부동 소수점 연산을 에뮬레이트하는 소프트웨어를 포함하여 IEEE 754 표준의 구현을 테스트하기위한 소프트웨어에 대해 설명합니다. 그러나 대부분의 정보는 C 또는 C ++에만 해당됩니다.
http://www.math.utah.edu/~beebe/software/ieee/
고정 점에 대한 참고 사항
이진 고정 소수점 숫자는 다음 네 가지 기본 산술 연산에서 알 수 있듯이 부동 소수점 대신 사용할 수 있습니다.
- 덧셈과 뺄셈은 간단합니다. 정수와 같은 방식으로 작동합니다. 그냥 더하거나 빼세요!
- 두 개의 고정 소수점 수를 곱하려면 두 수를 곱한 다음 정의 된 소수 비트 수만큼 오른쪽으로 이동합니다.
- 두 개의 고정 소수점 수를 나누려면 피제수를 정의 된 소수 비트 수만큼 왼쪽으로 이동 한 다음 제수로 나눕니다.
- 이 백서의 4 장 에는 이진 고정 소수점 수 구현에 대한 추가 지침 이 있습니다.
이진 고정 소수점 숫자는 int, long 및 BigInteger와 같은 정수 데이터 유형과 비 CLS 호환 유형 uint 및 ulong에서 구현 될 수 있습니다.
다른 답변에서 제안한 것처럼 테이블의 각 요소가 이진 고정 소수점 수인 조회 테이블을 사용하면 사인, 코사인, 제곱근 등과 같은 복잡한 함수를 구현할 수 있습니다. 룩업 테이블이 고정 소수점 수보다 세분화되지 않은 경우 룩업 테이블 세분화의 절반을 입력에 추가하여 입력을 반올림하는 것이 좋습니다.
// Assume each number has a 12 bit fractional part. (1/4096)
// Each entry in the lookup table corresponds to a fixed point number
// with an 8-bit fractional part (1/256)
input+=(1<<3); // Add 2^3 for rounding purposes
input>>=4; // Shift right by 4 (to get 8-bit fractional part)
// --- clamp or restrict input here --
// Look up value.
return lookupTable[input];
답변
이것이 C #의 문제입니까?
예. 다른 아키텍처로 인해 부동 소수점 표현의 부정확성 등의 프레임 속도 다른이 편차로 이어질 수 걱정거리를 – 그들은이 경우에도 동일한 (하나의 기계에 느린 GPU를 제외한 예를 들어 동일한 아키텍처) 부정확.
System.Decimal을 사용할 수 있습니까?
당신이 할 수없는 이유는 없지만 개가 느립니다.
프로그램을 배정도로 실행하는 방법이 있습니까?
예. CLR 런타임을 직접 호스팅하십시오 . CorBindToRuntimeEx를 호출하기 전에 모든 nessecary 호출 / 플래그 (부동 소수점 산술의 동작을 변경하는)를 C ++ 응용 프로그램으로 컴파일하십시오.
부동 소수점 계산을 일관성있게 유지하는 데 도움이되는 라이브러리가 있습니까?
내가 아는 한에서는 아니다.
이 문제를 해결하는 다른 방법이 있습니까?
전에이 문제를 해결했지만 아이디어는 QNumbers 를 사용하는 것 입니다. 그것들은 고정 소수점 형태의 실수입니다. 기수 -10 (10 진수)의 고정 소수점이 아닌 기수 -2 (이진수); 이 때문에 그것들의 수학적 프리미티브 (add, sub, mul, div)는 순진한 base-10 고정 점보다 훨씬 빠릅니다. 특히 n
두 값이 같은 경우 (귀하의 경우). 또한 통합되어 있기 때문에 모든 플랫폼에서 명확한 결과를 얻을 수 있습니다.
프레임 속도는 여전히 이것에 영향을 미칠 수 있지만 동기화 지점을 사용하여 나쁘지 않고 쉽게 수정됩니다.
QNumber에 더 많은 수학적 함수를 사용할 수 있습니까?
예, 이것을하기 위해 십진수를 왕복합니다. 또한 trig (sin, cos) 함수에 룩업 테이블 을 사용해야 합니다. 그것들은 실제로 다른 플랫폼에서 다른 결과를 줄 수 있으므로 올바르게 코딩하면 QNumber를 직접 사용할 수 있습니다.
답변
이 약간 오래된 MSDN 블로그 항목에 따르면 JIT는 부동 소수점에 SSE / SSE2를 사용하지 않으며 x87입니다. 그 때문에 언급했듯이 모드와 플래그에 대해 걱정해야하며 C #에서는 제어 할 수 없습니다. 따라서 정상적인 부동 소수점 연산을 사용한다고해서 프로그램의 모든 기계에서 동일한 결과가 보장되는 것은 아닙니다.
배정 밀도의 정확한 재현성을 얻으려면 소프트웨어 부동 소수점 (또는 고정 소수점) 에뮬레이션을 수행해야합니다. 이 작업을 수행하는 C # 라이브러리를 모르겠습니다.
필요한 작업에 따라 단 정밀도로 벗어날 수 있습니다. 아이디어는 다음과 같습니다.
- 관심있는 모든 값을 단 정밀도로 저장
- 작업을 수행하려면
- 입력을 배정도로 확장
- 배정도로 조작하다
- 결과를 단 정밀도로 다시 변환
x87의 큰 문제는 정밀도 플래그와 레지스터가 메모리에 유출되었는지 여부에 따라 53 비트 또는 64 비트 정확도로 계산이 수행 될 수 있다는 것입니다. 그러나 많은 작업에서 높은 정밀도로 작업을 수행하고 낮은 정밀도로 반올림하면 정답이 보장되므로 모든 시스템에서 정답이 동일하게 보장됩니다. 어느 쪽이든 정답을 보장하기에 충분한 정밀도를 가지므로 여분의 정밀도를 얻는 지 여부는 중요하지 않습니다.
이 체계에서 작동 해야하는 연산 : 더하기, 빼기, 곱하기, 나누기, sqrt. sin, exp 등과 같은 것은 작동하지 않습니다 (결과는 일반적으로 일치하지만 보장은 없습니다). “이중 반올림은 무해합니까?” ACM 참조 (유료 등록 요구)
도움이 되었기를 바랍니다!
답변
다른 답변에서 이미 언급했듯이 : 예, 이것은 순수한 Windows를 유지하는 경우에도 C #의 문제입니다.
해결책에 관해서 : 내장 BigInteger
클래스를 사용하고 그러한 수의 계산 / 저장에 공통 분모를 사용하여 모든 계산을 정의 된 정밀도로 스케일링 하면 문제를 줄일 수 있습니다 (그리고 약간의 노력 / 성능 저하로) .
OP의 요청에 따라 성능과 관련하여 :
System.Decimal
부호는 1 비트, 96 비트 정수 및 “스케일”(소수점이있는 위치)로 숫자를 나타냅니다. 모든 계산에서이 데이터 구조에서 작동해야하며 CPU에 내장 된 부동 소수점 명령어를 사용할 수 없습니다.
BigInteger
“솔루션”비슷한 않습니다 – 당신은 아마 당신이 80 비트 또는 정밀도의 240 개 비트를 원하는 … 당신이 /가 필요합니다 얼마나 많은 숫자 정의 할 수 있습니다 만있다.
CPU / FPU 내장 명령을 사용하지 않고 정수 전용 명령을 통해이 숫자에 대한 모든 작업을 항상 시뮬레이션해야하기 때문에 속도가 느려져 수학 연산 당 훨씬 더 많은 명령이 발생합니다.
성능 저하를 줄이려면 QNumbers (Jonathan Dickinson의 답변 참조- 부동 소수점 수학이 C #에서 일관됩니까? 가능합니까? ) 및 / 또는 캐싱 (예 : 삼각 계산 등)과 같은 몇 가지 전략 이 있습니다 .
답변
글쎄, 이 작업을 수행하는 방법 에 대한 나의 첫 번째 시도 는 다음과 같습니다.
- 중요한 부동 소수점 연산에 사용할 간단한 개체가있는 ATL.dll 프로젝트를 만듭니다. 부동 소수점을 수행하기 위해 xx87 이외의 하드웨어를 사용하지 않도록 설정하는 플래그로 컴파일해야합니다.
- 부동 소수점 연산을 호출하고 결과를 반환하는 함수를 만듭니다. 간단하게 시작한 다음 효과가있는 경우 나중에 필요한 경우 나중에 성능 요구를 충족시키기 위해 복잡성을 늘릴 수 있습니다.
- control_fp 호출을 실제 수학 주위에 두어 모든 머신에서 동일한 방식으로 수행되도록하십시오.
- 새 라이브러리를 참조하고 예상대로 작동하는지 테스트하십시오.
(32 비트 .dll로 컴파일 한 다음 x86 또는 AnyCpu (또는 64 비트 시스템에서 x86 만 타겟팅 할 수 있습니다. 아래 주석 참조)와 함께 사용할 수 있다고 생각합니다.)
그런 다음 작동한다고 가정하면 Mono를 사용하고 싶을 때 비슷한 방식으로 다른 x86 플랫폼에서 라이브러리를 복제 할 수 있다고 생각합니다 (물론 COM은 아니지만 와인을 사용합니까?) 우리는 거기에 간다 …).
작동한다고 가정하면 성능 문제를 해결하기 위해 한 번에 여러 작업을 수행 할 수있는 사용자 정의 기능을 설정할 수 있어야하며 플랫폼에서 최소한의 결과로 일관된 결과를 얻을 수있는 부동 소수점 연산이 있습니다. C ++로 작성된 코드의 나머지 부분을 C #으로 남겨 둡니다.