DC로 정밀도 손실


12

dc16 진수로 16 진수를 처리하는 데 사용하고 싶지만 정밀 문제가 발생합니다. 예를 들어, 아래 나는 곱 해요 F423F.FD100, 모두 진수를. 예상되는 대답은 F423FFD, 대신이주고 F423FFA.E1도 라운딩 후 가까운하지만, 정확한 충분히.

$ dc
16 d i o F423F.FD 100 * p
F423FFA.E1

나는 그것이 dc무제한 정밀 계산기 라는 것을 읽었 으며, 이것은 결코 큰 숫자가 아닙니다. 내가 잘못하고있는 것이 있습니까?

답변 주셔서 감사합니다. 의 문제를 감안할 때 dc총알을 물고 다른 기지의 실수에 대한 내 파서를 작성했습니다. 누구든지 코드에 관심이 있다면 여기에 게시 할 수 있습니다.

답변:


6

원래 번호 만 인쇄하면 반올림 된 것으로 표시됩니다.

$ dc <<<'16 d i o F423F.FD p'
F423F.FA

더 많은 정밀도를 위해 많은 후행 0을 추가하여 해결할 수 있습니다.

$ dc <<<'16 d i o F423F.FD000000 100 * p'
F423FFD.0000000

감사. 나는 그것을 dc사용 하기 위해 숫자를 마사지하고 파서를 직접 작성하기 위해 더 많은 코드를 취할 것이라고 생각합니다 ! (입력은 십진수를 갖거나 갖지 않을 수 있으며 다른 기수에있을 수 있으므로 패딩의 양이 다양합니다.)
Yimin Rong

2
나는 이것을 정답으로 표시 할 것이다. 책임이있는 사람들은 유지하는 단계 dc대답 에 제대로 비 소수점 소수 자릿수가 (모두 BC POSIX에 의해 지시로, 역사적 전통에 의해) 완전히 다른 DC와 BC가 사용하는 소수 규모의 모델보다 모델을 필요로 처리합니다. 따라서 기술적으로 수정 될 수는 dc있지만 bcWONTFIX로 분류 될 수 있습니다.
Yimin Rong

8

10 진수로 표시 ( dc변환에 사용)하면 999999.98 (반올림) × 256, 255999994.88 (16 진수 F423FFA.E1)에 해당합니다.

따라서 차이점은 dc반올림 동작 에서 비롯 됩니다 .256 × (999999 + 253 ÷ 256)을 계산하는 대신 255999997을 생성하고 253 ÷ 256을 반올림하고 결과를 곱합니다.

dc임의의 정밀도 계산기입니다. 즉, 원하는 정밀도로 계산할 수 있지만 그게 무엇인지 알려 주어야합니다. 기본적으로 정밀도는 0이며, 나눗셈은 정수 값만 생성하고 곱셈은 입력의 자릿수를 사용합니다. 정밀도를 설정하려면 다음을 사용하십시오 k(그리고 입력 또는 출력 기수에 관계없이 정밀도는 항상 10 진수로 표시됨).

10 k
16 d i o
F423FFD 100 / p
F423F.FD0000000
100 * p
F423FFD.000000000

(8 자리의 정밀도는 10 진수로 1 ÷ 256을 나타 내기 때문에 충분합니다.)


1
그것은 "임의 정밀도"계산기에 대해 전혀 예상치 못한 결과 인 것 같습니까?
Yimin Rong

3
k설정 하면 여전히 정밀도가 떨어집니다 : 10 k 16 d i o F423F.FD pF423F.FA,에서 사용하기 전에 모든 숫자를 확장해야합니다 dc. 기본적으로 어쨌든 미리 파싱합니다.
Yimin Rong

2
@Yimin 예, 불행히도 dc숫자 만 사용하여 입력의 배율을 조정합니다.이 숫자는 입력 기수를 사용하여 계산되지만 10 진수 값에 적용되기 때문에 나에게 버그처럼 보입니다.
Stephen Kitt

1
무슨의 @dhag POSIX의 지정 (용 bc있는 dc에 기초)는 ". 내부 연산이 입출력 기준에 관계없이, 지정된 소수 자릿수로, 10 진수의 경우를 실시한다"
Stephen Kitt

1
실제로 상수를 구문 분석하는 방법에 대한 문제입니다. 20 k 16 d i o 0.3 1 / p .19999999999999999를 인쇄 하십시오 . 동작은 단지 분할되어 있음을 이해 0.2하여 1(이론 값을 변경해서는 안된다). 20 k 16 d i o 0.3000 1 / p(정확하게) 인쇄 하는 동안 .30000000000000000. (계속)
NotAnUnixNazi

1

문제

문제는 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 진수 ( ) 수를 계산합니다 .
  • h4를 곱하십시오 .
  • h×4 - h = h × (4-1) = h × 3 = 3×h0을 추가하십시오 .

쉘 코드에서 (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*h16 진수 부동 소수점을 내부 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.FD50 자릿수로 확장하기 전에 상수를 읽기 위해 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

모든 이진 분수는 동일한 자릿수를 갖는 소수 분수 인 것으로 나타났다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.