ये संख्याएँ बराबर क्यों नहीं हैं?


273

निम्नलिखित कोड स्पष्ट रूप से गलत है। समस्या क्या है?

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

7
Stackoverflow.com/q/6874867 और stackoverflow.com/q/2769510 भी देखें । आर इन्फर्नो भी एक और महान पढ़ा है।
हारून ने

1
साइट-व्यापी भाषा-अज्ञेय Q और A: फ्लोटिंग पॉइंट गणित टूट गया है?
ग्रेगर थॉमस

dplanet, मैंने सभी संभावित मामलों ("<=", "> =", "=") के लिए नीचे दिए गए दोहरे परिशुद्धता अंकगणित में एक समाधान जोड़ा। आशा करता हूँ की ये काम करेगा।
एर्दोगन CEVHER

जवाबों:


355

सामान्य (भाषा अज्ञेय) कारण

चूंकि आईईईई फ्लोटिंग पॉइंट अंकगणित में सभी संख्याओं का बिल्कुल प्रतिनिधित्व नहीं किया जा सकता है (मानक जो लगभग सभी कंप्यूटर दशमलव संख्याओं का प्रतिनिधित्व करने और उनके साथ गणित करने के लिए उपयोग करते हैं), आपको हमेशा वह नहीं मिलेगा जिसकी आपको उम्मीद थी। यह विशेष रूप से सच है क्योंकि कुछ मान जो सरल हैं, परिमित दशमलव (जैसे कि 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

कुछ और विवरण, सीधे एक समान प्रश्न के उत्तर से कॉपी किए गए :

आपके सामने जो समस्या है वह यह है कि फ्लोटिंग पॉइंट ज्यादातर मामलों में दशमलव अंशों का प्रतिनिधित्व नहीं कर सकता है, जिसका अर्थ है कि आप अक्सर पाएंगे कि सटीक मिलान विफल हो जाते हैं।

जब आप कहते हैं कि आर थोड़ा झूठ है:

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"

आप देख सकते हैं कि ये संख्या अलग-अलग हैं, लेकिन प्रतिनिधित्व थोड़ा सा है। यदि हम उन्हें बाइनरी में देखते हैं (अच्छी तरह से, हेक्स, जो समकक्ष है) तो हमें एक स्पष्ट तस्वीर मिलती है:

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
 #....
 #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पूरे वैक्टर की तुलना एक इकाई के रूप में की जाती है।

पिछले उदाहरणों का उपयोग करना

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

आर सांख्यिकीय कंप्यूटिंग के लिए एक मुफ्त सॉफ्टवेयर वातावरण है ??
किटीगर्ल

41

ब्रायन की टिप्पणी में जोड़ना (जो कारण है) आप 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

जोशुआ की चेतावनी के अनुसार यहाँ अद्यतन कोड है (धन्यवाद जोशुआ):

 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

17
all.equalFALSEमतभेद होने पर वापस नहीं आता है , इसलिए आपको बयान isTRUEमें इसका उपयोग करते समय इसे लपेटने की आवश्यकता होती है if
जोशुआ उलरिच

12

यह हैकिश है, लेकिन जल्दी:

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

2
लेकिन आप all.equal(... tolerance)पैरामीटर का उपयोग कर सकते हैं । all.equal(0.147, 0.15, tolerance=0.05)सच हैं।
एसएमसीआई

10

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()


0

मुझे भी ऐसी ही समस्या का समाधान करना पड़ा था। मैंने निम्नलिखित समाधान का उपयोग किया।

@ मुझे यह काम असमान कटौती के अंतराल के समाधान के आसपास मिला। @ मैंने 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

0

सामान्य रूप से तुलना ("<=", "> =", "=") दोहरे एकीकरण अंकगणित में:

तुलना करना </ 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

तुलना करना => 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

तुलना करना = बी

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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.