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


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


1
একটি সাইট-বিস্তৃত ভাষা-অজ্ঞাত প্রশ্ন এবং উত্তর: ভাসমান পয়েন্ট গণিত কি ভেঙে গেছে?
গ্রেগর টমাস

dplanet, আমি নীচের ডাবল স্পষ্টতা পাটিগণিত সমস্ত তুলনা ক্ষেত্রে ("<=", "> =", "=") জন্য একটি সমাধান যুক্ত করেছি। আশা করি এটা সাহায্য করবে.
এরদোগান সিইভিভার

উত্তর:


355

সাধারণ (ভাষা অজ্ঞেয়) কারণে

যেহেতু আইইইইই ভাসমান পয়েন্ট গণিতগুলিতে সমস্ত সংখ্যা হুবহু উপস্থাপন করা যায় না (প্রায় সমস্ত কম্পিউটার দশমিক সংখ্যার প্রতিনিধিত্ব করতে এবং তাদের সাথে গণিত করে এমন স্ট্যান্ডার্ড), আপনি সর্বদা আপনার প্রত্যাশাটি পাবেন না। এটি বিশেষত সত্য কারণ কিছু মান যা সাধারণ, সীমাবদ্ধ দশমিক (যেমন ০.০ এবং ০.০৫) কম্পিউটারে ঠিক উপস্থাপন করা হয় না এবং সুতরাং গাণিতিকের ফলাফলগুলি কোনও ফলাফল দিতে পারে না যা প্রত্যক্ষভাবে উপস্থাপনের অনুরূপ " পরিচিত "উত্তর।

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

স্কেলারের তুলনা করা

এটির মধ্যে স্ট্যান্ডার্ড সমাধানটি 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 এর কাছাকাছি, যেমন এটি।

আর এর মেশিনের ক্ষেত্রটি দেখে এই ক্ষুদ্রতম প্রতিনিধিত্বযোগ্য নম্বরটি আমরা কোনও প্রদত্ত কম্পিউটারের জন্য জানতে পারি :

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


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

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

@ আমি এই কাজটি অসম কাটা বিরতি সম্পর্কে সমাধানের আশেপাশে পেয়েছি। @ আমি আর-তে রাউন্ড ফাংশনটি ব্যবহার করেছি 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

ডাবল প্রাক্কলিত গাণিতিকের সাধারণ তুলনা ("<=", "> =", "="):

একটি <= বি: তুলনা করা

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

একটি>> বি তুলনা করা:

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
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.