Programing

이 숫자가 왜 다른가요?

lottogame 2020. 4. 1. 07:56
반응형

이 숫자가 왜 다른가요?


다음 코드는 분명히 잘못되었습니다. 뭐가 문제 야?

i <- 0.1
i <- i + 0.05
i
## [1] 0.15
if(i==0.15) cat("i equals 0.15") else cat("i does not equal 0.15")
## i does not equal 0.15

일반적인 (언어에 구애받지 않는) 이유

모든 숫자를 IEEE 부동 소수점 산술 (거의 모든 컴퓨터가 10 진수를 나타내고 수학으로 사용하는 표준)으로 정확하게 표현할 수있는 것은 아니므로 항상 예상 한 값을 얻지는 못할 수 있습니다. 이것은 단순하고 유한 한 소수 (예 : 0.1 및 0.05) 인 일부 값이 컴퓨터에 정확하게 표시되지 않아서 산술 결과가 ""의 직접 표현과 동일한 결과를 제공하지 않을 수 있기 때문에 특히 그렇습니다. 알려진 "답변입니다.

이것은 컴퓨터 산술의 잘 알려진 제한 사항이며 여러 곳에서 논의됩니다.

스칼라 비교

이에 대한 표준 솔루션 R은을 사용하는 ==것이 아니라 all.equal기능 을 사용 하는 것입니다. 또는 all.equal차이가있는 경우 차이점에 대해 자세히 설명하므로 isTRUE(all.equal(...)).

if(isTRUE(all.equal(i,0.15))) cat("i equals 0.15") else cat("i does not equal 0.15")

수확량

i equals 0.15

all.equal대신에 몇 가지 사용 예가 있습니다 ==(마지막 예는 이것이 올바르게 차이점을 보여줄 것입니다).

0.1+0.05==0.15
#[1] FALSE
isTRUE(all.equal(0.1+0.05, 0.15))
#[1] TRUE
1-0.1-0.1-0.1==0.7
#[1] FALSE
isTRUE(all.equal(1-0.1-0.1-0.1, 0.7))
#[1] TRUE
0.3/0.1 == 3
#[1] FALSE
isTRUE(all.equal(0.3/0.1, 3))
#[1] TRUE
0.1+0.1==0.15
#[1] FALSE
isTRUE(all.equal(0.1+0.1, 0.15))
#[1] FALSE

더 자세한 내용 은 비슷한 질문에 대한 답변 에서 직접 복사되었습니다 .

발생하는 문제는 대부분의 경우 부동 소수점이 소수를 정확하게 표현할 수 없다는 것입니다. 즉, 정확한 일치가 실패하는 경우가 많습니다.

당신이 말할 때 R은 약간 있습니다 :

1.1-0.2
#[1] 0.9
0.9
#[1] 0.9

실제로 어떻게 생각하는지 십진수로 확인할 수 있습니다.

sprintf("%.54f",1.1-0.2)
#[1] "0.900000000000000133226762955018784850835800170898437500"
sprintf("%.54f",0.9)
#[1] "0.900000000000000022204460492503130808472633361816406250"

이 숫자가 다르다는 것을 알 수 있지만 표현은 다루기 힘듭니다. 우리가 그것들을 이진으로 보면 (음, 16 진, 이에 상응하는) 더 명확한 그림을 얻을 수 있습니다 :

sprintf("%a",0.9)
#[1] "0x1.ccccccccccccdp-1"
sprintf("%a",1.1-0.2)
#[1] "0x1.ccccccccccccep-1"
sprintf("%a",1.1-0.2-0.9)
#[1] "0x1p-53"

2^-53이 숫자가로 다른 것을 알 수 있습니다. 이 숫자는 값이 1에 가까운 두 숫자 사이의 가장 작은 차이이기 때문에 중요합니다.

우리는 주어진 컴퓨터에서 R의 machine field 에서이 가장 작은 숫자가 무엇인지 알아낼 수 있습니다 :

 ?.Machine
 #....
 #double.eps     the smallest positive floating-point number x 
 #such that 1 + x != 1. It equals base^ulp.digits if either 
 #base is 2 or rounding is 0; otherwise, it is 
 #(base^ulp.digits) / 2. Normally 2.220446e-16.
 #....
 .Machine$double.eps
 #[1] 2.220446e-16
 sprintf("%a",.Machine$double.eps)
 #[1] "0x1p-52"

이 사실을 사용하여 차이가 부동 소수점에서 가장 작은 표현 가능한 숫자에 가까운 지 확인하는 '거의 같음'함수를 만들 수 있습니다. 실제로 이것은 이미 존재합니다 : all.equal.

?all.equal
#....
#all.equal(x,y) is a utility to compare R objects x and y testing ‘near equality’.
#....
#all.equal(target, current,
#      tolerance = .Machine$double.eps ^ 0.5,
#      scale = NULL, check.attributes = TRUE, ...)
#....

따라서 all.equal 함수는 실제로 숫자 사이의 차이가 두 가수 사이의 가장 작은 차이의 제곱근인지 확인합니다.

이 알고리즘은 denormals라고하는 극소수 근처에서는 약간 재미 있지만 걱정할 필요는 없습니다.

벡터 비교

위의 논의는 두 개의 단일 값의 비교를 가정했습니다. R에는 스칼라가 없으며 벡터 만 있으며 암시 적 벡터화는 언어의 강점입니다. 벡터의 값을 요소별로 비교하기 위해 이전 원칙이 적용되지만 구현 방식이 약간 다릅니다. 전체 벡터를 단일 엔티티로 비교하는 ==동안 벡터화됩니다 (요소 별 비교 수행) all.equal.

이전 예제 사용

a <- c(0.1+0.05, 1-0.1-0.1-0.1, 0.3/0.1, 0.1+0.1)
b <- c(0.15,     0.7,           3,       0.15)

=="예상 된"결과를 제공 all.equal하지 않으며 요소별로 수행하지 않습니다.

a==b
#[1] FALSE FALSE FALSE FALSE
all.equal(a,b)
#[1] "Mean relative difference: 0.01234568"
isTRUE(all.equal(a,b))
#[1] FALSE

오히려 두 벡터를 반복하는 버전을 사용해야합니다

mapply(function(x, y) {isTRUE(all.equal(x, y))}, a, b)
#[1]  TRUE  TRUE  TRUE FALSE

이 기능 버전이 필요한 경우 작성할 수 있습니다

elementwise.all.equal <- Vectorize(function(x, y) {isTRUE(all.equal(x, y))})

그냥 호출 할 수 있습니다

elementwise.all.equal(a, b)
#[1]  TRUE  TRUE  TRUE FALSE

또는 all.equal더 많은 함수 호출 을 래핑 하는 대신 관련 내부를 복제하고 all.equal.numeric암시 적 벡터화를 사용할 수 있습니다.

tolerance = .Machine$double.eps^0.5
# this is the default tolerance used in all.equal,
# but you can pick a different tolerance to match your needs

abs(a - b) < tolerance
#[1]  TRUE  TRUE  TRUE FALSE

이것은에 의해 취해진 접근법이며 dplyr::near,

부동 소수점 숫자의 두 벡터가 (쌍별) 동일한 지 비교하는 안전한 방법입니다. ==공차가 내장되어 있기 때문에를 사용하는 것보다 안전 합니다.

dplyr::near(a, b)
#[1]  TRUE  TRUE  TRUE FALSE

Brian의 의견에 추가하면 (이유가 있음) all.equal대신 다음 을 사용하여이 문제를 극복 할 수 있습니다 .

# i <- 0.1
# i <- i + 0.05
# i
#if(all.equal(i, .15)) cat("i equals 0.15\n") else cat("i does not equal 0.15\n")
#i equals 0.15

여기에 Joshua의 경고는 업데이트 된 코드입니다 (Joshua에게 감사합니다).

 i <- 0.1
 i <- i + 0.05
 i
if(isTRUE(all.equal(i, .15))) { #code was getting sloppy &went to multiple lines
    cat("i equals 0.15\n") 
} else {
    cat("i does not equal 0.15\n")
}
#i equals 0.15

이것은 hackish이지만 빠릅니다.

if(round(i, 10)==0.15) cat("i equals 0.15") else cat("i does not equal 0.15")

dplyr::near()부동 소수점 숫자의 두 벡터가 같은지 테스트하기위한 옵션입니다. 이것은 문서 의 예입니다 .

sqrt(2) ^ 2 == 2
#> [1] FALSE
library(dplyr)
near(sqrt(2) ^ 2, 2)
#> [1] TRUE

이 기능에는 공차 매개 변수가 내장되어 tol = .Machine$double.eps^0.5있으며 조정할 수 있습니다. 기본 매개 변수는의 기본값과 동일합니다 all.equal().


나는 비슷한 문제가 있었다. 나는 다음 해결책을 사용했다.

@ 불균일 한 컷 간격에 대한 해결책을 찾았습니다. @ R에서 반올림 기능을 사용했습니다. 옵션을 2 자리로 설정해도 문제가 해결되지 않았습니다.

options(digits = 2)
cbind(
  seq(      from = 1, to = 9, by = 1 ), 
  cut( seq( from = 1, to = 9, by = 1),          c( 0, 3, 6, 9 ) ),
  seq(      from = 0.1, to = 0.9, by = 0.1 ), 
  cut( seq( from = 0.1, to = 0.9, by = 0.1),    c( 0, 0.3, 0.6, 0.9 )),
  seq(      from = 0.01, to = 0.09, by = 0.01 ), 
  cut( seq( from = 0.01, to = 0.09, by = 0.01),    c( 0, 0.03, 0.06, 0.09 ))
)

옵션 (숫자 = 2)에 따른 불균등 한 절단 간격 출력 :

  [,1] [,2] [,3] [,4] [,5] [,6]
 [1,]    1    1  0.1    1 0.01    1
 [2,]    2    1  0.2    1 0.02    1
 [3,]    3    1  0.3    2 0.03    1
 [4,]    4    2  0.4    2 0.04    2
 [5,]    5    2  0.5    2 0.05    2
 [6,]    6    2  0.6    2 0.06    3
 [7,]    7    3  0.7    3 0.07    3
 [8,]    8    3  0.8    3 0.08    3
 [9,]    9    3  0.9    3 0.09    3


options(digits = 200)
cbind(
  seq(      from = 1, to = 9, by = 1 ), 
  cut( round(seq( from = 1, to = 9, by = 1), 2),          c( 0, 3, 6, 9 ) ),
  seq(      from = 0.1, to = 0.9, by = 0.1 ), 
  cut( round(seq( from = 0.1, to = 0.9, by = 0.1), 2),    c( 0, 0.3, 0.6, 0.9 )),
  seq(      from = 0.01, to = 0.09, by = 0.01 ), 
  cut( round(seq( from = 0.01, to = 0.09, by = 0.01), 2),    c( 0, 0.03, 0.06, 0.09 ))
)

라운드 기능을 기준으로 동일한 컷 간격 출력 :

      [,1] [,2] [,3] [,4] [,5] [,6]
 [1,]    1    1  0.1    1 0.01    1
 [2,]    2    1  0.2    1 0.02    1
 [3,]    3    1  0.3    1 0.03    1
 [4,]    4    2  0.4    2 0.04    2
 [5,]    5    2  0.5    2 0.05    2
 [6,]    6    2  0.6    2 0.06    2
 [7,]    7    3  0.7    3 0.07    3
 [8,]    8    3  0.8    3 0.08    3
 [9,]    9    3  0.9    3 0.09    3

이중 전치 산술의 일반화 된 비교 ( "<=", "> =", "=") :

a <= b 비교 :

IsSmallerOrEqual <- function(a,b) {   
# Control the existence of "Mean relative difference..." in all.equal; 
# if exists, it results in character, not logical:
if (   class(all.equal(a, b)) == "logical" && (a<b | all.equal(a, b))) { return(TRUE)
 } else if (a < b) { return(TRUE)
     } else { return(FALSE) }
}

IsSmallerOrEqual(abs(-2-(-2.2)), 0.2) # TRUE
IsSmallerOrEqual(abs(-2-(-2.2)), 0.3) # TRUE
IsSmallerOrEqual(abs(-2-(-2.2)), 0.1) # FALSE
IsSmallerOrEqual(3,3); IsSmallerOrEqual(3,4); IsSmallerOrEqual(4,3) 
# TRUE; TRUE; FALSE

a> = b 비교 :

IsBiggerOrEqual <- function(a,b) {
# Control the existence of "Mean relative difference..." in all.equal; 
# if exists, it results in character, not logical:
if (   class(all.equal(a, b)) == "logical" && (a>b | all.equal(a, b))) { return(TRUE)
 } else if (a > b) { return(TRUE)
     } else { return(FALSE) }
}
IsBiggerOrEqual(3,3); IsBiggerOrEqual(4,3); IsBiggerOrEqual(3,4) 
# TRUE; TRUE; FALSE

a = b 비교 :

IsEqual <- function(a,b) {
# Control the existence of "Mean relative difference..." in all.equal; 
# if exists, it results in character, not logical:
if (   class(all.equal(a, b)) == "logical" ) { return(TRUE)
 } else { return(FALSE) }
}

IsEqual(0.1+0.05,0.15) # TRUE

참고 URL : https://stackoverflow.com/questions/9508518/why-are-these-numbers-not-equal

반응형