निम्नलिखित कोड स्पष्ट रूप से गलत है। समस्या क्या है?
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
निम्नलिखित कोड स्पष्ट रूप से गलत है। समस्या क्या है?
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
जवाबों:
चूंकि आईईईई फ्लोटिंग पॉइंट अंकगणित में सभी संख्याओं का बिल्कुल प्रतिनिधित्व नहीं किया जा सकता है (मानक जो लगभग सभी कंप्यूटर दशमलव संख्याओं का प्रतिनिधित्व करने और उनके साथ गणित करने के लिए उपयोग करते हैं), आपको हमेशा वह नहीं मिलेगा जिसकी आपको उम्मीद थी। यह विशेष रूप से सच है क्योंकि कुछ मान जो सरल हैं, परिमित दशमलव (जैसे कि 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
ब्रायन की टिप्पणी में जोड़ना (जो कारण है) आप 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
all.equal
FALSE
मतभेद होने पर वापस नहीं आता है , इसलिए आपको बयान isTRUE
में इसका उपयोग करते समय इसे लपेटने की आवश्यकता होती है if
।
यह हैकिश है, लेकिन जल्दी:
if(round(i, 10)==0.15) cat("i equals 0.15") else cat("i does not equal 0.15")
all.equal(... tolerance)
पैरामीटर का उपयोग कर सकते हैं । all.equal(0.147, 0.15, tolerance=0.05)
सच हैं।
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
सामान्य रूप से तुलना ("<=", "> =", "=") दोहरे एकीकरण अंकगणित में:
तुलना करना </ 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