r - কেন এই সংখ্যা সমান নয়?




floating-point floating-accuracy (4)

নিম্নলিখিত কোড সম্ভবত ভুল। সমস্যা কি?

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 ভাসমান বিন্দু গাণিতিক (আদর্শ যা প্রায়শই কম্পিউটারগুলি দশমিক সংখ্যা প্রতিনিধিত্ব করে এবং তাদের সাথে গণিত করতে ব্যবহৃত হয়) তে প্রতিনিধিত্ব করা যেতে পারে, তাই আপনি যা আশা করেন তা সর্বদা আপনার কাছে পাবেন না। এটি বিশেষভাবে সত্য কারণ কিছু মান যা সহজ, সীমাবদ্ধ দশমিক (যেমন 0.1 এবং 0.05) কম্পিউটারে ঠিকভাবে উপস্থাপিত হয় না এবং তাই তাদের গাণিতিক ফলাফলগুলি এমন ফলাফল দেয় না যা সরাসরি " পরিচিত "উত্তর।

এটি কম্পিউটার গণিতের একটি সুপরিচিত সীমাবদ্ধতা এবং বিভিন্ন স্থানে আলোচনা করা হয়:

Scalars তুলনা

এই ক্ষেত্রে স্ট্যান্ডার্ড সমাধান == ব্যবহার করা হয় না, বরং সব all.equal ফাংশন। অথবা বরং, যদি 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 ক্ষেত্রের দিকে তাকিয়ে:

 ?.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 ফাংশন প্রকৃতপক্ষে চেক করছে যে সংখ্যার মধ্যে পার্থক্য দুটি মন্টিসাসের মধ্যে ক্ষুদ্রতম পার্থক্যের বর্গমূল।

এই অ্যালগরিদম ডিনরমালস নামক অত্যন্ত ছোট সংখ্যার কাছাকাছি কিছুটা মজার হয়ে যায়, তবে আপনাকে সে সম্পর্কে চিন্তা করতে হবে না।

ভেক্টর তুলনা

উপরোক্ত আলোচনা দুটি একক মান তুলনা। আর, কোন scalars আছে, শুধু ভেক্টর এবং অন্তর্নিহিত ভেক্টরাইজেশন ভাষা একটি শক্তি। ভ্যাক্টরগুলির মূল্য তুলনামূলকভাবে তুলনা করার জন্য পূর্ববর্তী নীতিগুলি ধরে রাখা, তবে বাস্তবায়নটি সামান্য ভিন্ন। == হল ভেক্টরাইজড (একটি উপাদান ভিত্তিক তুলনা), যখন 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

পরিবর্তে, দুটি ভেক্টর উপর loops একটি সংস্করণ ব্যবহার করা আবশ্যক

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

আমারও একই সমস্যা ছিল। আমি নিম্নলিখিত সমাধান ব্যবহৃত।

@ আমি অসামান্য কাটা বিরতি সম্পর্কে সমাধান প্রায় এই কাজ খুঁজে পাওয়া যায় নি। @ আমি র মধ্যে বৃত্তাকার ফাংশন ব্যবহার। বিকল্পটি 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

ব্রায়ান এর মন্তব্যটি যুক্ত করা (যা কারণ) আপনি all.equal পরিবর্তে 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

dplyr::near() ভাসমান বিন্দু সংখ্যার দুটি ভেক্টর সমান হলে পরীক্ষার জন্য একটি বিকল্প। এই docs থেকে উদাহরণ:

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

ফাংশন একটি সহনশীলতা প্যারামিটার নির্মিত হয়েছে: tol = .Machine$double.eps^0.5 যে সামঞ্জস্য করা যাবে। ডিফল্ট প্যারামিটারটি all.equal() জন্য ডিফল্ট হিসাবে একই।





r-faq