this SO question에 대한 답변은 구현의 측면에서 매우 포괄적이다. 저기서 보았던 것보다 더 많은 설명이 있습니다 :
하나의 접근법은 모든 숫자를 [-1.0,1.0]과 같이 범위로 강제하는 것입니다. 그런 다음 해당 숫자를 [-2^15, (2^15) -1] 범위에 매핑합니다. 이 두 숫자를 곱하면 예를 들어,
Half = round(0.5*32768); //16384
Third = round((1.0/3.0)*32768); //10923
당신이 마지막 줄에 32768에 의해
Temp = Half*Third; //178962432
Result = Temp/32768; //5461 = round(1.0/6.0)*32768
디바이 딩을 얻을은 추가 확장 단계를 필요로 곱셈에 대해 만든 점 Patros입니다. 이것은 2^N 스케일링을 명시 적으로 작성하는 것이 더 합리적입니다.
x1 = x1Float*(2^15);
x2 = x2Float*(2^15);
Temp = x1Float*x2Float*(2^15)*(2^15);
Result = Temp/(2^15); //get back to 2^N scaling
그래서 산술입니다. 구현을 위해 두 개의 16 비트 정수를 곱하면 32 비트 결과가 필요하므로 Temp는 32 비트 여야합니다. 또한 32768은 16 비트 변수에서 표현할 수 없으므로 컴파일러가 32 비트 바로 가기를 생성합니다. 당신이 이미 언급 한 바와 같이, 당신은 그래서 당신은 권리 범위가 아닌)
N = 15;
SInt16 x1 = round(x1Float * (1 << N));
SInt16 x2 = round(x2Float * (1 << N));
SInt32 Temp = x1*x2;
Result = (SInt16)(Temp >> N);
FloatResult = ((double)Result)/(1 << N);
쓰기 그러나 [-1,1을 가정 할 수 2의 거듭 제곱으로/분할을 곱 전환 할 수 있습니다? 숫자를 [-4.0,4.0]으로 제한하려면 N = 13을 사용할 수 있습니다. 그런 다음 1 비트의 부호 비트와 2 진 소수점 이전의 2 비트 및 13 비트의 부호 비트가 있습니다. 이들은 각각 1.15 및 3.13 고정 소수점 분수 유형이라고합니다. 헤드 룸에 대한 분수의 정밀도를 교환합니다.
분수 형식을 더하고 빼면 채도가 떨어질 때까지 잘 작동합니다. 파토 로스 (Patros)가 말했듯이, 분할에 대해서는 실제로 스케일링이 취소됩니다. 그래서 당신은
Quotient = (SInt16)(((SInt32)x1 << N)/x2); //x1 << N needs wide storage
배가 및 전체 숫자로 나누어 정상적으로 작동 정밀도를 유지하기 위해,
Quotient = (x1/x2) << N;
을해야하거나. 예를 들어, 정수를 추가 및 뺀, 당신이 간단하게
Quotient = x1/6; //equivalent to x1Float*(2^15)/6, stays scaled
그리고 2의 거듭 제곱으로 나누어의 경우
를 작성할 수 6
Quotient = x1 >> 3; //divides by 8, can't do x1 << -3 as Patros pointed out
을 분할하지만, 순진 작동하지 않습니다 . 정수가 x.y 유형에 맞는지, 등가 분수 유형을 만들어 계속 진행하는지 먼저 확인해야합니다.
이 아이디어가 도움이되기를 바랍니다. 깨끗한 구현을위한 다른 질문의 코드를 살펴보십시오.
그건 내가 생각한거야. 하지만 고정 점을 얻으려면 2의 힘으로 번식하지 않겠습니까? 그것은 단지 비트를 이동하는 것을 포함합니다. –
부호있는 int를 사용할 때 쉬프트가 펑키 할 수 있습니다. 2의 제곱은 더 효율적이지만 디버그하기는 더 어렵습니다. 고정 소수점을 처음 사용하는 경우 10의 제곱을 추천합니다. – patros
답변 해 주셔서 감사합니다. float-less ADSR 봉투를 구현하는 우아한 방법을 아직 찾지 못했습니다. 방금 샘플을 오른쪽 시프 팅하여 임의의 2의 제곱으로 나눗셈을 시도했기 때문에 내 진폭이 줄어 듭니다. 그러나 그와 함께 부드러운 봉투를 만드는 방법을 알 수는 없습니다. –