出于某种原因,我需要比较两个基元:long和float。
我可以使用以下代码吗?
long a = 111L;
float b = 111.1f
if (a > b) {
...
}
我知道,浮点数和浮点数可以与使用epsilon值等进行比较。
但我怎样才能更准确地比较我的情况?
感谢所有人。
答案 0 :(得分:3)
你可以将它们都包裹在BigDecimal
中,并将它们进行比较:
long a = 111L;
float b = 111.1f;
BigDecimal first = new BigDecimal(a);
BigDecimal second = new BigDecimal(b, MathContext.DECIMAL32);
if (first.compareTo(second) > 0) { ... }
为了理解为什么我们有兴趣在同一类型下拥有两个操作数,让我们深入研究JLS 5.6.2 Binary Numeric Promotion:
当运算符将二进制数字提升应用于一对操作数时,每个操作数必须表示可转换为数字类型的值,以下规则适用:
如果任何操作数属于引用类型,则将其取消装箱 转换(第5.1.8节)。
- 醇>
应用扩展基元转换(第5.1.2节)来转换 或者由以下规则指定的两个操作数:
如果任一操作数的类型为
double
,则另一个操作数将转换为double
。否则,如果任一操作数的类型为
float
,则转换另一个操作数 到float
。否则,如果任一操作数的类型为
long
,则转换另一个操作数 到long
。否则,两个操作数都将转换为
int
类型。
有了这个,我们可以得出结论,对于比较a > b
,long
操作数将被隐式提升为float
。但是,这可能会导致精度损失,如JLS 5.1.2 Widening primitive conversion中所述:
将
int
或long
值扩展为float
或更长的转换 值加倍,可能导致精度损失 - 即结果 可能会丢失该值的一些最低有效位。在这 case,生成的浮点值将是正确舍入的 使用IEEE 754舍入到最接近模式的整数值的版本 (§4.2.4)。
答案 1 :(得分:1)
如果您想使用epsilon,您可以
>>> class A(object):
... def __str__(self):
... return 'a','b'
...
>>> a = A()
>>> print a.__str__()
('a', 'b')
>>> print str(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __str__ returned non-string (type tuple)
或
// allow for the smallest rounding error
if (a > b + Math.sign(b) * Float.ulp(b))
如果您可以假设非负数,则可以删除Math.sign(b)。
但是,在这种情况下使用BigDecimal可能会更清楚,请参阅@ kocko的回答。
BTW:提高准确性的最简单的变化是使用// assume six digits of precision
static final double ERR = 1e-6;
// allow for the smallest rounding error
if (a > b + Math.sign(b) * b * ERR)
代替double
。 float
的准确度几乎提高了五十亿倍,除非你拥有数十亿,否则你使用的提取记忆无关紧要。
答案 2 :(得分:0)
Java支持直接比较long和float。但是,有一些警告:
如果比较long和float,则long被转换为float,因为float被认为是比long更宽的类型。但是,此转换可能会失去精度:long值将转换为最接近的float值;因此,对于较长的a
和一个b
a > b
,如果a
实际上是b
b
大于b
,则BigDecimal
可能为假最接近BigDecimal
的值。
每个long和float值都可以用long a = ...;
float b = ...;
if (BigDecimal.valueOf(a).compareTo(BigDecimal.valueOf(b)) > 0) {
...
}
表示,所以你也可以使用Logger.log(JSON.stringify(new_arr));
类来比较long和float:
CREATE TABLE #TABLE
(
[WEEK] INT,
[WEEK_DATA] VARCHAR(100)
)
INSERT INTO #TABLE
SELECT 1, 'DATA FOR WEEK 1' UNION
SELECT 3, 'DATA FOR WEEK 3' UNION
SELECT 10, 'DATA FOR WEEK 10'
SELECT * FROM #TABLE
---
DECLARE @WEEK INT
SET @WEEK = 1
DECLARE @DUMMY TABLE
(
[WEEK] INT
)
WHILE @WEEK <= 52
BEGIN
INSERT INTO @DUMMY
SELECT @WEEK
SET @WEEK = @WEEK + 1
END
SELECT
T1.[WEEK],
WEEK_DATA = CASE WHEN T2.WEEK_DATA IS NULL THEN 'NO DATA' ELSE T2.WEEK_DATA END
FROM
@DUMMY AS T1
LEFT JOIN
#TABLE AS T2 ON T1.[WEEK] = T2.[WEEK]
答案 3 :(得分:0)
轻松使用此代码:
Double.compare(longVar * 1., floatVar)