문제
문제는 dc (및 bc)가 숫자 상수를 이해하는 방식입니다.
예를 들어 값 (16 진수) 0.3
(1로 나눈 값)은 다음에 가까운 값으로 변환됩니다.0.2
$ dc <<<"20k 16 d i o 0.3 1 / p"
.199999999999999999999999999
실제로 일반 상수도 0.3
변경됩니다.
$ dc <<<"20 k 16 d i o 0.3 p"
.1
그것은 이상한 방식으로 보이지만 (더 나중에는) 아닙니다.
0을 더 추가하면 정답이 올바른 값에 접근합니다.
$ dc <<<"20 k 16 d i o 0.30 p"
.2E
$ dc <<<"20 k 16 d i o 0.300 p"
.2FD
$ dc <<<"20 k 16 d i o 0.3000 p"
.3000
마지막 값은 정확하며 0을 더 추가하는 방법에 관계없이 정확합니다.
$ dc <<<"20 k 16 d i o 0.30000000 p"
.3000000
이 문제는 bc에도 있습니다.
$ bc <<< "scale=20; obase=16; ibase=16; 0.3 / 1"
.19999999999999999
$ bc <<< "scale=20; obase=16; ibase=16; 0.30 / 1"
.2E147AE147AE147AE
$ bc <<< "scale=20; obase=16; ibase=16; 0.300 / 1"
.2FDF3B645A1CAC083
$ bc <<< "scale=20; obase=16; ibase=16; 0.3000 / 1"
.30000000000000000
비트 당 한 자리?
부동 소수점 숫자에 대한 직관적이지 않은 사실은 필요한 자릿수 (도트 뒤)가 이진 비트 수 (도트 뒤)와 동일하다는 것입니다. 이진수 0.101은 정확히 0.625와 같습니다. 이진수 0.0001110001은 (정확하게) 0.1103515625
(10 진수 )와 같습니다.
$ bc <<<'scale=30;obase=10;ibase=2; 0.101/1; 0.0001110001/1'; echo ".1234567890"
.625000000000000000000000000000
.110351562500000000000000000000
.1234567890
또한 2 ^ (-10)과 같은 부동 소수점 숫자의 경우 이진수에는 비트가 하나만 있습니다 (세트).
$ bc <<<"scale=20; a=2^-10; obase=2;a; obase=10; a"
.0000000001000000000000000000000000000000000000000000000000000000000
.00097656250000000000
이진수 .0000000001
(10)와 10 진수 .0009765625
(10)의 수가 같습니다. 다른 기저에서는 그렇지 않을 수 있지만 기저 10은 dc와 bc에서 숫자의 내부 표현이므로 우리가 실제로 신경 써야 할 유일한 기저입니다.
수학 증명은이 답변의 끝에 있습니다.
기원전 규모
도트 뒤의 자릿수는 내장 함수 scale()
형식 bc 로 계산할 수 있습니다 .
$ bc <<<'obase=16;ibase=16; a=0.FD; scale(a); a; a*100'
2
.FA
FA.E1
표시된 바와 같이, 상수를 나타내는 데 2 자리가 충분하지 않습니다 0.FD
.
또한 점 다음에 사용 된 문자 수를 계산하는 것은 숫자의 스케일을보고하고 사용하는 매우 잘못된 방법입니다. 숫자의 스케일 (모든베이스에서)은 필요한 비트 수를 계산해야합니다.
16 진 플로트의 이진수.
알려진 바와 같이, 각 16 진수는 4 비트를 사용합니다. 따라서 소수점 이하의 각 16 진수에는 4 개의 이진수가 필요합니다. 위의 (홀수?) 사실로 인해 4 개의 소수점이 필요합니다.
따라서 같은 0.FD
숫자는 8 진수로 올바르게 표현되어야합니다.
$ bc <<<'obase=10;ibase=16;a=0.FD000000; scale(a);a;a*100'
8
.98828125
253.00000000
제로 추가
수학은 간단합니다 (16 진수) :
h
점 뒤 의 16 진수 ( ) 수를 계산합니다 .
h
4를 곱하십시오 .
h×4 - h = h × (4-1) = h × 3 = 3×h
0을 추가하십시오 .
쉘 코드에서 (sh의 경우) :
a=F423F.FD
h=${a##*.}
h=${#h}
a=$a$(printf '%0*d' $((3*h)) 0)
echo "$a"
echo "obase=16;ibase=16;$a*100" | bc
echo "20 k 16 d i o $a 100 * p" | dc
다음과 같이 인쇄됩니다 (dc 및 bc에서 모두 올바르게).
$ sh ./script
F423F.FD000000
F423FFD.0000000
F423FFD.0000000
내부적으로 bc (또는 dc)는 3*h
16 진수 부동 소수점을 내부 10 진수 표현으로 변환하기 위해 필요한 자릿수를 위에서 계산 한 숫자 ( ) 와 일치시킬 수 있습니다. 또는 다른베이스에 대한 일부 다른 기능 (기타 수는 다른베이스의베이스 10과 bc 및 dc의 내부와 관련하여 유한하다고 가정). 2 i (2,4,8,16, ...)와 5,10 처럼 .
posix
posix 사양에 다음과 같은 내용이 있습니다 (bc의 경우 dc의 기준).
내부 계산은 입력 및 출력 기준에 관계없이 10 진수로 지정된 소수 자릿수로 수행됩니다.
그러나 "… 지정된 소수 자릿수" "10 진 내부 계산"에 영향을주지 않으면 서 "… 상수를 나타내는 데 필요한 소수 자릿수"(상술 한 바와 같이)로 이해 될 수있다.
때문에:
bc <<<'scale=50;obase=16;ibase=16; a=0.FD; a+1'
1.FA
bc는 위에서 설정 한대로 50 ( "지정된 소수 자릿수")을 실제로 사용하지 않습니다.
분할 된 경우에만 변환됩니다 ( 0.FD
50 자릿수로 확장하기 전에 상수를 읽기 위해 2의 스케일을 사용하므로 여전히 잘못됨 ).
$ bc <<<'scale=50;obase=16;ibase=16; a=0.FD/1; a'
.FAE147AE147AE147AE147AE147AE147AE147AE147A
그러나 이것은 정확합니다.
$ bc <<<'scale=50;obase=16;ibase=16; a=0.FD000000/1; a'
.FD0000000000000000000000000000000000000000
다시 말하지만, 숫자 문자열 (상수)을 읽으려면 올바른 비트 수를 사용해야합니다.
수학 증명
두 단계로 :
이진 분수는 a / 2 n 으로 쓸 수 있습니다
이진 분수는 음의 거듭 제곱의 유한 합입니다.
예를 들면 다음과 같습니다.
= 0.00110101101 =
= 0. 0 0 1 1 0 1 0 1 1 0 1
= 0 + 0 × (2) -1 + 0 × 2 -2 + 1 × 2 -3 + 1 × 2 -4 + 0 × 2 -5 + 1 × 2 -6 + 0 × 2 -7 + 1 × 2 -8 + 1 × 2-9 + 0 × 2-10 + 1 × 2-11
= 2 -3 + 2 -4 + 2 -6 + 2 -8 + 2 -13 + 2 -11 = (제로 제거하여)
n 비트의 이진 분수에서 마지막 비트의 값은 2 -n 또는 1/2 n 입니다. 이 예에서는 2-11 또는 1/2 11 입니다.
= 1/2 3 + 1/2 4 + 1/2 6 + 1/2 8 + 1/2 9 + 1/2 11 = (반전)
일반적으로 분모 는 양의 분자 지수가 2 인 경우 분모가 2n 이 될 수 있습니다. 그런 다음 모든 항을 단일 값 a / 2 n 으로 결합 할 수 있습니다 . 이 예의 경우 :
2 = 8 / 2 (11) + 2 7 / 2 11 + 2 5 / 2 11 + 2 3 / 2 11 + 2 (2) / 2 11 + 2 11 = ((2)로 나타내어 11 )
= (2 8 + 2 7 + 2 5 + 2 3 + 2 2 + 1) / 2 11 = (공통 인자 추출)
= (256 + 128 + 32 + 8 + 4 + 1) / 2 11 = (값으로 변환)
= 429/2 11
모든 이진 분수는 b / 10 n으로 표현할 수 있습니다
a / 2 n 에 5 n
/ 5 n을 곱하여 (a × 5 n ) / (2 n × 5 n ) = (a × 5 n ) / 10 n = b / 10 n을 얻습니다 . 여기서 b = a × 5 n . n 자리입니다.
예를 들면 다음과 같습니다.
(429 · 5 11 ) / 10 11 = 20947265625/10 11 = 0.20947265625
모든 이진 분수는 동일한 자릿수를 갖는 소수 분수 인 것으로 나타났다.
dc
사용 하기 위해 숫자를 마사지하고 파서를 직접 작성하기 위해 더 많은 코드를 취할 것이라고 생각합니다 ! (입력은 십진수를 갖거나 갖지 않을 수 있으며 다른 기수에있을 수 있으므로 패딩의 양이 다양합니다.)