整数反直觉测试问题:R语言整数判断函数对45×1.4返回FALSE
Great question—this is a classic floating-point precision quirk that catches even experienced R users off guard! Let’s break down exactly why your is.wholeNumber function fails with 45*1.4, and how to fix it.
What’s Really Happening with 45*1.4
At first glance, 45 * 1.4 should equal 63 exactly. But binary floating-point numbers (which R uses for numeric values) can’t represent every decimal fraction perfectly. Let’s peek at the actual value stored:
print(45 * 1.4, digits = 20) # Output: 62.99999999999999289457
That tiny trailing decimal is why your function returns FALSE: floor(45*1.4) becomes 62, and 62.99999999999999... isn’t equal to 62.
Why Your Original Function Fails
Your function is.wholeNumber <- function(x) x == floor(x) relies on exact equality, which is risky with floating-point numbers. Even calculations that should result in an integer can have tiny precision errors due to how decimals are converted to binary.
Fixing the Integer Check
Instead of checking for exact equality, we should check if the value is "close enough" to an integer, within a small tolerance (called epsilon). Here are a few robust approaches:
1. Use a Tolerance Threshold
Compare the absolute difference between the value and its rounded version to a small number (like 1e-10, which is way smaller than any meaningful decimal you’d work with):
is.wholeNumber <- function(x) { abs(x - round(x)) < 1e-10 } # Test it out is.wholeNumber(45*1.4) # Returns TRUE is.wholeNumber(63) # Returns TRUE is.wholeNumber(63.1) # Returns FALSE
2. Use all.equal() for Flexible Comparison
R’s built-in all.equal() function accounts for floating-point precision by default, so you can use it to check if the value matches its rounded integer:
is.wholeNumber <- function(x) { isTRUE(all.equal(x, round(x))) }
Wrapping it in isTRUE() ensures you get a clean boolean output instead of a character message about differences.
3. Use dplyr::near() (If You’re Using the Tidyverse)
The near() function from dplyr is designed specifically for this kind of floating-point comparison:
library(dplyr) is.wholeNumber <- function(x) { near(x, round(x)) }
Key Takeaway
Never rely on exact equality (==) when working with floating-point numbers. Always use a tolerance-based check to account for tiny precision errors that come from decimal-to-binary conversion.
内容的提问来源于stack exchange,提问作者owen88

